// 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 #include "assembly.hpp" #include "appdomain.hpp" #include "perfcounters.h" #include "assemblyname.hpp" #include "eeprofinterfaces.h" #include "reflectclasswriter.h" #include "comdynamic.h" #include #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" #include "customattribute.h" #include "winnls.h" #include "caparser.h" #include "../md/compiler/custattr.h" #include "mdaassistants.h" #include "peimagelayout.inl" // 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), 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_fIsDomainNeutral(pDomain == SharedDomain::GetDomain()), #ifdef FEATURE_LOADER_OPTIMIZATION m_bMissingDependenciesCheckDone(FALSE), #endif // FEATURE_LOADER_OPTIMIZATION m_debuggerFlags(debuggerFlags), m_fTerminated(FALSE), m_HostAssemblyId(0) #ifdef FEATURE_COMINTEROP , m_InteropAttributeStatus(INTEROP_ATTRIBUTE_UNSET) #endif #ifdef FEATURE_PREJIT , m_isInstrumentedStatus(IS_INSTRUMENTED_UNSET) #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") //---------------------------------------------------------------------------------------------- // 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); 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); // 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; 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(this)); if (this->m_fTerminated) return; 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 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 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 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 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); // 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(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); } NewHolder 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, pLoaderAllocator); } // Start loading process { // Create a concrete assembly // (!Do not remove scoping brace: order is important here: the Assembly holder must destruct before the AllocMemTracker!) NewHolder 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; // Set the additional strong name information pAssem->SetStrongNameLevel(Assembly::SN_NONE); if (publicKey.GetSize() > 0) { pAssem->SetStrongNameLevel(Assembly::SN_PUBLIC_KEY); { // 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 == 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 // would be REALLY nice to unify this with main loading loop pDomainAssembly->Begin(); pDomainAssembly->SetAssembly(pAssem); pDomainAssembly->m_level = FILE_LOAD_ALLOCATE; pDomainAssembly->DeliverSyncEvents(); pDomainAssembly->DeliverAsyncEvents(); pDomainAssembly->FinishLoad(); pDomainAssembly->ClearLoading(); pDomainAssembly->m_level = FILE_ACTIVE; } { 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 #endif // CROSSGEN_COMPILE void Assembly::SetDomainAssembly(DomainAssembly *pDomainAssembly) { CONTRACTL { PRECONDITION(CheckPointer(pDomainAssembly)); THROWS; GC_TRIGGERS; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; GetManifestModule()->SetDomainFile(pDomainAssembly); } // 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); } #ifndef DACCESS_COMPILE void Assembly::SetParent(BaseDomain* pParent) { LIMITED_METHOD_CONTRACT; m_pDomain = pParent; } #endif // !DACCCESS_COMPILE mdFile Assembly::GetManifestFileToken(LPCSTR name) { return mdFileNil; } 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; } DomainAssembly * pDomainAssembly = pModule->LoadAssembly( ::GetAppDomain(), tkType, szNamespace, szClassName); 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() { } //@TODO: if module is not signed it needs to acquire the //permissions from the assembly. 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); } //***************************************************************************** // 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; } return m_pFriendAssemblyDescriptor->IgnoresAccessChecksTo(pAccessedAssembly); } #ifndef CROSSGEN_COMPILE enum CorEntryPointType { EntryManagedMain, // void main(String[]) EntryCrtMain // unsigned main(void) }; 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)) { return E_INVALIDARG; } 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; } 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); SetLatchedExitCode(*pParam->piRetVal); } GCPROTECT_END(); // // When we get mainCRTStartup from the C++ then this should be able to go away. 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) { { #ifdef FEATURE_COMINTEROP GCX_PREEMP(); Thread::ApartmentState state = Thread::AS_Unknown; state = SystemDomain::GetEntryPointThreadAptState(pMeth->GetMDImport(), pMeth->GetMemberDef()); // If the entry point has an explicit thread apartment state, set it // before running the AppDomainManager initialization code. if (state == Thread::AS_InSTA || state == Thread::AS_InMTA) SystemDomain::SetThreadAptState(state); #endif // FEATURE_COMINTEROP } RunMainPre(); // 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()); 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 __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); } 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; // This will set the value of m_isInstrumentedStatus by calling IsInstrumentedHelper() // that method performs string pattern matching using the Config value of ZapBBInstr // We cache the value returned from that method in m_isInstrumentedStatus // if (m_isInstrumentedStatus == IS_INSTRUMENTED_UNSET) { EX_TRY { FAULT_NOT_FATAL(); if (IsInstrumentedHelper()) { m_isInstrumentedStatus = IS_INSTRUMENTED_TRUE; } else { m_isInstrumentedStatus = IS_INSTRUMENTED_FALSE; } } EX_CATCH { m_isInstrumentedStatus = IS_INSTRUMENTED_FALSE; } EX_END_CATCH(RethrowTerminalExceptions); } // At this point m_isInstrumentedStatus can't have the value of IS_INSTRUMENTED_UNSET _ASSERTE(m_isInstrumentedStatus != IS_INSTRUMENTED_UNSET); return (m_isInstrumentedStatus == IS_INSTRUMENTED_TRUE); } 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()->HasNativeOrReadyToRunImage()) 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(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)); PRECONDITION(CheckPointer(pAssemEmitter, NULL_NOT_OK)); POSTCONDITION(!IsNilToken(RETVAL)); POSTCONDITION(TypeFromToken(RETVAL) == mdtAssemblyRef); } CONTRACT_END; SafeComHolder emitHolder; 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(); } 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; hr = COR_E_NOTSUPPORTED; 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; }; 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_LOADER_OPTIMIZATION 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 pWinMDImport; if (SUCCEEDED(m_pManifest->GetMDImport()->QueryInterface(IID_IWinMDImport, (void **)&pWinMDImport))) { if (InterlockedCompareExchangeT(&m_pManifestWinMDImport, pWinMDImport, NULL) == NULL) { pWinMDImport.SuppressRelease(); } } } return m_pManifestWinMDImport; } #endif // FEATURE_COMINTEROP #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(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(itFullAccessAssemblies.GetElement()); delete pFriendAssemblyName; } } //--------------------------------------------------------------------------------------- // // 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 pFriendAssemblies = new FriendAssemblyDescriptor; // We're going to do this twice, once for InternalsVisibleTo and once for IgnoresAccessChecks ReleaseHolder 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(&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; StackScratchBuffer buffer; pFriendAssemblyName = new FriendAssemblyName_t; hr = pFriendAssemblyName->Init(displayName.GetUTF8(buffer)); if (SUCCEEDED(hr)) { hr = pFriendAssemblyName->CheckFriendAssemblyName(); } 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. 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; AssemblySpec asmDef; asmDef.InitializeSpec(pAssembly); ArrayList::ConstIterator itAssemblyNames = alAssemblyNames.Iterate(); while (itAssemblyNames.Next()) { const FriendAssemblyName_t *pFriendAssemblyName = static_cast(itAssemblyNames.GetElement()); HRESULT hr = AssemblySpec::RefMatchesDef(pFriendAssemblyName, &asmDef) ? S_OK : S_FALSE; if (hr == S_OK) { return true; } } return false; } #endif // !DACCESS_COMPILE