// 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. // -------------------------------------------------------------------------------- // DomainFile.cpp // // -------------------------------------------------------------------------------- #include "common.h" // -------------------------------------------------------------------------------- // Headers // -------------------------------------------------------------------------------- #include #include "invokeutil.h" #include "eeconfig.h" #include "dynamicmethod.h" #include "field.h" #include "dbginterface.h" #include "eventtrace.h" #ifdef FEATURE_PREJIT #include #include "compile.h" #endif // FEATURE_PREJIT #include "dllimportcallback.h" #include "peimagelayout.inl" #include "winrthelpers.h" #ifdef FEATURE_PERFMAP #include "perfmap.h" #endif // FEATURE_PERFMAP BOOL DomainAssembly::IsUnloading() { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; BOOL fIsUnloading = FALSE; fIsUnloading = this->GetAppDomain()->IsUnloading(); if (!fIsUnloading) { fIsUnloading = m_fDebuggerUnloadStarted; } return fIsUnloading; } #ifndef DACCESS_COMPILE DomainFile::DomainFile(AppDomain *pDomain, PEFile *pFile) : m_pDomain(pDomain), m_pFile(pFile), m_pOriginalFile(NULL), m_pModule(NULL), m_level(FILE_LOAD_CREATE), m_pError(NULL), m_notifyflags(NOT_NOTIFIED), m_loading(TRUE), m_pDynamicMethodTable(NULL), m_pUMThunkHash(NULL), m_bDisableActivationCheck(FALSE), m_dwReasonForRejectingNativeImage(0) { CONTRACTL { CONSTRUCTOR_CHECK; THROWS; // From CreateHandle GC_NOTRIGGER; MODE_ANY; FORBID_FAULT; } CONTRACTL_END; m_hExposedModuleObject = NULL; pFile->AddRef(); } DomainFile::~DomainFile() { CONTRACTL { DESTRUCTOR_CHECK; NOTHROW; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; m_pFile->Release(); if(m_pOriginalFile) m_pOriginalFile->Release(); if (m_pDynamicMethodTable) m_pDynamicMethodTable->Destroy(); delete m_pError; } #endif //!DACCESS_COMPILE LoaderAllocator * DomainFile::GetLoaderAllocator() { CONTRACTL { NOTHROW; GC_NOTRIGGER; SO_TOLERANT; MODE_ANY; } CONTRACTL_END; Assembly *pAssembly = GetDomainAssembly()->GetAssembly(); if ((pAssembly != NULL) && (pAssembly->IsCollectible())) { return pAssembly->GetLoaderAllocator(); } else { return this->GetAppDomain()->GetLoaderAllocator(); } } #ifndef DACCESS_COMPILE void DomainFile::ReleaseFiles() { WRAPPER_NO_CONTRACT; Module* pModule=GetCurrentModule(); if(pModule) pModule->StartUnload(); if (m_pFile) m_pFile->ReleaseIL(); if(m_pOriginalFile) m_pOriginalFile->ReleaseIL(); if(pModule) pModule->ReleaseILData(); } BOOL DomainFile::TryEnsureActive() { CONTRACT(BOOL) { INSTANCE_CHECK; THROWS; GC_TRIGGERS; } CONTRACT_END; BOOL success = TRUE; EX_TRY { EnsureActive(); } EX_CATCH { success = FALSE; } EX_END_CATCH(RethrowTransientExceptions); RETURN success; } // Optimization intended for EnsureLoadLevel only #include void DomainFile::EnsureLoadLevel(FileLoadLevel targetLevel) { CONTRACT_VOID { INSTANCE_CHECK; THROWS; GC_TRIGGERS; } CONTRACT_END; TRIGGERSGC (); if (IsLoading()) { this->GetAppDomain()->LoadDomainFile(this, targetLevel); // Enforce the loading requirement. Note that we may have a deadlock in which case we // may be off by one which is OK. (At this point if we are short of targetLevel we know // we have done so because of reentrancy contraints.) RequireLoadLevel((FileLoadLevel)(targetLevel-1)); } else ThrowIfError(targetLevel); RETURN; } #include void DomainFile::AttemptLoadLevel(FileLoadLevel targetLevel) { CONTRACT_VOID { INSTANCE_CHECK; THROWS; GC_TRIGGERS; } CONTRACT_END; if (IsLoading()) this->GetAppDomain()->LoadDomainFile(this, targetLevel); else ThrowIfError(targetLevel); RETURN; } CHECK DomainFile::CheckLoadLevel(FileLoadLevel requiredLevel, BOOL deadlockOK) { CONTRACTL { INSTANCE_CHECK; NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; if (deadlockOK) { #ifndef CROSSGEN_COMPILE // CheckLoading requires waiting on a host-breakable lock. // Since this is only a checked-build assert and we've been // living with it for a while, I'll leave it as is. //@TODO: CHECK statements are *NOT* debug-only!!! CONTRACT_VIOLATION(ThrowsViolation|GCViolation|TakesLockViolation); CHECK(this->GetAppDomain()->CheckLoading(this, requiredLevel)); #endif } else { CHECK_MSG(m_level >= requiredLevel, "File not sufficiently loaded"); } CHECK_OK; } void DomainFile::RequireLoadLevel(FileLoadLevel targetLevel) { CONTRACT_VOID { INSTANCE_CHECK; THROWS; GC_TRIGGERS; } CONTRACT_END; if (GetLoadLevel() < targetLevel) { ThrowIfError(targetLevel); ThrowHR(MSEE_E_ASSEMBLYLOADINPROGRESS); // @todo: better exception } RETURN; } void DomainFile::SetError(Exception *ex) { CONTRACT_VOID { PRECONDITION(!IsError()); PRECONDITION(ex != NULL); INSTANCE_CHECK; THROWS; GC_TRIGGERS; POSTCONDITION(IsError()); } CONTRACT_END; m_pError = new ExInfo(ex->DomainBoundClone()); GetCurrentModule()->NotifyEtwLoadFinished(ex->GetHR()); if (!IsProfilerNotified()) { SetProfilerNotified(); #ifdef PROFILING_SUPPORTED if (GetCurrentModule() != NULL && !GetCurrentModule()->GetAssembly()->IsDomainNeutral()) { // Only send errors for non-shared assemblies; other assemblies might be successfully completed // in another app domain later. GetCurrentModule()->NotifyProfilerLoadFinished(ex->GetHR()); } #endif } RETURN; } void DomainFile::ThrowIfError(FileLoadLevel targetLevel) { CONTRACT_VOID { INSTANCE_CHECK; MODE_ANY; THROWS; GC_TRIGGERS; } CONTRACT_END; if (m_level < targetLevel) { if (m_pError) m_pError->Throw(); } RETURN; } CHECK DomainFile::CheckNoError(FileLoadLevel targetLevel) { LIMITED_METHOD_CONTRACT; CHECK(m_level >= targetLevel || !IsError()); CHECK_OK; } CHECK DomainFile::CheckLoaded() { CONTRACTL { INSTANCE_CHECK; NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; CHECK_MSG(CheckNoError(FILE_LOADED), "DomainFile load resulted in an error"); if (IsLoaded()) CHECK_OK; // Mscorlib is allowed to run managed code much earlier than other // assemblies for bootstrapping purposes. This is because it has no // dependencies, security checks, and doesn't rely on loader notifications. if (GetFile()->IsSystem()) CHECK_OK; CHECK_MSG(GetFile()->CheckLoaded(), "PEFile has not been loaded"); CHECK_OK; } CHECK DomainFile::CheckActivated() { CONTRACTL { INSTANCE_CHECK; NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; CHECK_MSG(CheckNoError(FILE_ACTIVE), "DomainFile load resulted in an error"); if (IsActive()) CHECK_OK; // Mscorlib is allowed to run managed code much earlier than other // assemblies for bootstrapping purposes. This is because it has no // dependencies, security checks, and doesn't rely on loader notifications. if (GetFile()->IsSystem()) CHECK_OK; CHECK_MSG(GetFile()->CheckLoaded(), "PEFile has not been loaded"); CHECK_MSG(IsLoaded(), "DomainFile has not been fully loaded"); CHECK_MSG(m_bDisableActivationCheck || CheckLoadLevel(FILE_ACTIVE), "File has not had execution verified"); CHECK_OK; } #endif //!DACCESS_COMPILE DomainAssembly *DomainFile::GetDomainAssembly() { CONTRACTL { SUPPORTS_DAC; NOTHROW; GC_NOTRIGGER; SO_TOLERANT; } CONTRACTL_END; _ASSERTE(IsAssembly()); return (DomainAssembly *) this; } BOOL DomainFile::IsIntrospectionOnly() { WRAPPER_NO_CONTRACT; return GetFile()->IsIntrospectionOnly(); } // Return true iff the debugger should get notifications about this assembly. // // Notes: // The debuggee may be stopped while a DomainAssmebly is being initialized. In this time window, // GetAssembly() may be NULL. If that's the case, this function has to return FALSE. Later on, when // the DomainAssembly is fully initialized, this function will return TRUE. This is the only scenario // where this function is mutable. In other words, a DomainAssembly can only change from being invisible // to visible, but NOT vice versa. Once a DomainAssmebly is fully initialized, this function should be // immutable for an instance of a module. That ensures that the debugger gets consistent // notifications about it. It this value mutates, than the debugger may miss relevant notifications. BOOL DomainAssembly::IsVisibleToDebugger() { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; // If you can't run an assembly, then don't send notifications to the debugger. // This check includeds IsIntrospectionOnly(). return ((GetAssembly() != NULL) ? GetAssembly()->HasRunAccess() : FALSE); } #ifndef DACCESS_COMPILE #ifdef FEATURE_PREJIT void DomainFile::ExternalLog(DWORD level, const WCHAR *fmt, ...) { WRAPPER_NO_CONTRACT; va_list args; va_start(args, fmt); GetOriginalFile()->ExternalVLog(LF_ZAP, level, fmt, args); va_end(args); } void DomainFile::ExternalLog(DWORD level, const char *msg) { WRAPPER_NO_CONTRACT; GetOriginalFile()->ExternalLog(level, msg); } #endif #ifndef CROSSGEN_COMPILE //--------------------------------------------------------------------------------------- // // Returns managed representation of the module (Module or ModuleBuilder). // Returns NULL if the managed scout was already collected (see code:LoaderAllocator#AssemblyPhases). // OBJECTREF DomainFile::GetExposedModuleObject() { CONTRACTL { INSTANCE_CHECK; THROWS; MODE_COOPERATIVE; GC_TRIGGERS; } CONTRACTL_END; LoaderAllocator * pLoaderAllocator = GetLoaderAllocator(); if (m_hExposedModuleObject == NULL) { // Atomically create a handle LOADERHANDLE handle = pLoaderAllocator->AllocateHandle(NULL); FastInterlockCompareExchangePointer(&m_hExposedModuleObject, handle, static_cast(NULL)); } if (pLoaderAllocator->GetHandleValue(m_hExposedModuleObject) == NULL) { REFLECTMODULEBASEREF refClass = NULL; // Will be TRUE only if LoaderAllocator managed object was already collected and therefore we should // return NULL BOOL fIsLoaderAllocatorCollected = FALSE; GCPROTECT_BEGIN(refClass); if (GetFile()->IsDynamic()) { refClass = (REFLECTMODULEBASEREF) AllocateObject(MscorlibBinder::GetClass(CLASS__MODULE_BUILDER)); } else { refClass = (REFLECTMODULEBASEREF) AllocateObject(MscorlibBinder::GetClass(CLASS__MODULE)); } refClass->SetModule(m_pModule); // Attach the reference to the assembly to keep the LoaderAllocator for this collectible type // alive as long as a reference to the module is kept alive. if (GetModule()->GetAssembly() != NULL) { OBJECTREF refAssembly = GetModule()->GetAssembly()->GetExposedObject(); if ((refAssembly == NULL) && GetModule()->GetAssembly()->IsCollectible()) { fIsLoaderAllocatorCollected = TRUE; } refClass->SetAssembly(refAssembly); } pLoaderAllocator->CompareExchangeValueInHandle(m_hExposedModuleObject, (OBJECTREF)refClass, NULL); GCPROTECT_END(); if (fIsLoaderAllocatorCollected) { // The LoaderAllocator managed object was already collected, we cannot re-create it // Note: We did not publish the allocated Module/ModuleBuilder object, it will get collected // by GC return NULL; } } return pLoaderAllocator->GetHandleValue(m_hExposedModuleObject); } // DomainFile::GetExposedModuleObject #endif // CROSSGEN_COMPILE BOOL DomainFile::DoIncrementalLoad(FileLoadLevel level) { STANDARD_VM_CONTRACT; if (IsError()) return FALSE; Thread *pThread; pThread = GetThread(); _ASSERTE(pThread); INTERIOR_STACK_PROBE_FOR(pThread, 8); switch (level) { case FILE_LOAD_BEGIN: Begin(); break; case FILE_LOAD_FIND_NATIVE_IMAGE: #ifdef FEATURE_PREJIT FindNativeImage(); #endif break; case FILE_LOAD_VERIFY_NATIVE_IMAGE_DEPENDENCIES: #ifdef FEATURE_PREJIT VerifyNativeImageDependencies(); #endif break; case FILE_LOAD_ALLOCATE: Allocate(); break; case FILE_LOAD_ADD_DEPENDENCIES: AddDependencies(); break; case FILE_LOAD_PRE_LOADLIBRARY: PreLoadLibrary(); break; case FILE_LOAD_LOADLIBRARY: LoadLibrary(); break; case FILE_LOAD_POST_LOADLIBRARY: PostLoadLibrary(); break; case FILE_LOAD_EAGER_FIXUPS: EagerFixups(); break; case FILE_LOAD_VTABLE_FIXUPS: VtableFixups(); break; case FILE_LOAD_DELIVER_EVENTS: DeliverSyncEvents(); break; case FILE_LOADED: FinishLoad(); break; case FILE_LOAD_VERIFY_EXECUTION: VerifyExecution(); break; case FILE_ACTIVE: Activate(); break; default: UNREACHABLE(); } END_INTERIOR_STACK_PROBE; #ifdef FEATURE_MULTICOREJIT { Module * pModule = GetModule(); if (pModule != NULL) // Should not triggle assert when module is NULL { this->GetAppDomain()->GetMulticoreJitManager().RecordModuleLoad(pModule, level); } } #endif return TRUE; } #ifdef FEATURE_PREJIT void DomainFile::VerifyNativeImageDependencies(bool verifyOnly) { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; PRECONDITION(verifyOnly || (m_pDomain->GetDomainFileLoadLevel(this) == FILE_LOAD_FIND_NATIVE_IMAGE)); } CONTRACTL_END; // This function gets called multiple times. The first call is the real work. // Subsequent calls are only to verify that everything still looks OK. if (!verifyOnly) ClearNativeImageStress(); if (!m_pFile->HasNativeImage()) { CheckZapRequired(); return; } { // Go through native dependencies & make sure they still have their prejit images after // the security check. // NOTE: we could theoretically do this without loading the dependencies, if we cache the // COR_TRUST structures from the dependencies in the version information. // // Verify that all of our hard dependencies are loaded at the right base address. // If not, abandon prejit image (or fix ours up) // Also, if there are any hard dependencies, then our native image also needs to be // loaded at the right base address // Note: we will go through all of our dependencies, call Load on them, and check the base // addresses & identity. // It is important to note that all of those dependencies are also going to do the // same thing, so we might conceivably check a base address as OK, and then have that image // abandoned by that assembly during its VerifyNativeImageDependencies phase. // However, we avoid this problem since the hard depedencies stored are a closure of the // hard dependencies of an image. This effectively means that our check here is a superset // of the check that the dependencies will perform. Even if we hit a dependency loop, we // will still guarantee that we've examined all of our dependencies. ReleaseHolder pNativeImage = m_pFile->GetNativeImageWithRef(); if(pNativeImage==NULL) { CheckZapRequired(); return; } PEImageLayout* pNativeLayout = pNativeImage->GetLoadedLayout(); // reuse same codepath for both manifest and non-manifest modules ReleaseHolder pManifestNativeImage(NULL); PEFile* pManifestFile = m_pFile; PEImageLayout* pManifestNativeLayout = pNativeLayout; if (!IsAssembly()) { pManifestFile = GetDomainAssembly()->GetCurrentAssembly() ->GetManifestModule()->GetFile(); pManifestNativeImage = pManifestFile->GetNativeImageWithRef(); if (pManifestNativeImage == NULL) { ExternalLog(LL_ERROR, "Rejecting native image because there is no " "ngen image for manifest module. Check why the manifest module " "does not have an ngen image"); m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_NoNiForManifestModule; STRESS_LOG3(LF_ZAP,LL_INFO100,"Rejecting native file %p, because its manifest module %p has no NI - reason 0x%x\n",pNativeImage.GetValue(),pManifestFile,m_dwReasonForRejectingNativeImage); goto NativeImageRejected; } return; } COUNT_T cDependencies; CORCOMPILE_DEPENDENCY *pDependencies = pManifestNativeLayout->GetNativeDependencies(&cDependencies); LOG((LF_ZAP, LL_INFO100, "ZAP: Checking native image dependencies for %S.\n", pNativeImage->GetPath().GetUnicode())); for (COUNT_T iDependency = 0; iDependency < cDependencies; iDependency++) { CORCOMPILE_DEPENDENCY *pDependency = &(pDependencies[iDependency]); // Later, for domain neutral assemblies, we will also want to verify security policy // in such cases, the prejit image should store the publisher info for the dependencies // for us. // If this is not a hard-bound dependency, then skip to the next dependency if (pDependency->signNativeImage == INVALID_NGEN_SIGNATURE) continue; // // CoreCLR hard binds to mscorlib.dll only. Avoid going through the full load. // #ifdef _DEBUG AssemblySpec name; name.InitializeSpec(pDependency->dwAssemblyRef, ((pManifestNativeImage != NULL) ? pManifestNativeImage : pNativeImage)->GetNativeMDImport(), GetDomainAssembly()); _ASSERTE(name.IsMscorlib()); #endif PEAssembly * pDependencyFile = SystemDomain::SystemFile(); ReleaseHolder pDependencyNativeImage = pDependencyFile->GetNativeImageWithRef(); if (pDependencyNativeImage == NULL) { ExternalLog(LL_ERROR, W("Rejecting native image because dependency %s is not native"), pDependencyFile->GetPath().GetUnicode()); m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_DependencyNotNative; STRESS_LOG3(LF_ZAP,LL_INFO100,"Rejecting native file %p, because dependency %p is not NI - reason 0x%x\n",pNativeImage.GetValue(),pDependencyFile,m_dwReasonForRejectingNativeImage); goto NativeImageRejected; } PTR_PEImageLayout pDependencyNativeLayout = pDependencyNativeImage->GetLoadedLayout(); // Assert that the native image signature is as expected // Fusion will ensure this CORCOMPILE_VERSION_INFO * pDependencyNativeVersion = pDependencyNativeLayout->GetNativeVersionInfo(); LoggablePEAssembly logAsm(pDependencyFile); if (!RuntimeVerifyNativeImageDependency(pDependency, pDependencyNativeVersion, &logAsm)) goto NativeImageRejected; } LOG((LF_ZAP, LL_INFO100, "ZAP: Native image dependencies for %S OK.\n", pNativeImage->GetPath().GetUnicode())); return; } NativeImageRejected: m_pFile->ClearNativeImage(); m_pFile->SetCannotUseNativeImage(); CheckZapRequired(); return; } BOOL DomainFile::IsZapRequired() { CONTRACTL { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; if (!m_pFile->HasMetadata() || !g_pConfig->RequireZap(GetSimpleName())) return FALSE; #if defined(_DEBUG) && defined(FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS) // If we're intentionally treating NIs as if they were MSIL assemblies, and the test // is flexible enough to accept that (e.g., complus_zaprequired=2), then zaps are not // required (i.e., it's ok for m_pFile->m_nativeImage to be NULL), but only if we // loaded an actual NI to be treated as an IL assembly if (PEFile::ShouldTreatNIAsMSIL()) { // Since the RequireZap() call above returned true, we know that some level of // zap requiredness was configured _ASSERTE(g_pConfig->RequireZaps() != EEConfig::REQUIRE_ZAPS_NONE); // If config uses this special value (2), zaps are not required, so long as // we're using an actual NI as IL if ((g_pConfig->RequireZaps() == EEConfig::REQUIRE_ZAPS_ALL_JIT_OK) && m_pFile->HasOpenedILimage() && m_pFile->GetOpenedILimage()->HasNativeHeader()) { return FALSE; } } #endif // defined(_DEBUG) && defined(FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS) // Does this look like a resource-only assembly? We assume an assembly is resource-only // if it contains no TypeDef (other than the TypeDef) and no MethodDef. // Note that pMD->GetCountWithTokenKind(mdtTypeDef) doesn't count the type. IMDInternalImportHolder pMD = m_pFile->GetMDImport(); if (pMD->GetCountWithTokenKind(mdtTypeDef) == 0 && pMD->GetCountWithTokenKind(mdtMethodDef) == 0) return FALSE; DomainAssembly * pDomainAssembly = GetDomainAssembly(); // If the manifest module does not have an ngen image, the non-manifest // modules cannot either if (m_pFile->IsModule() && !pDomainAssembly->GetFile()->CanUseNativeImage()) m_pFile->SetCannotUseNativeImage(); // Some cases are not supported by design. They can never have a native image. // So ignore such cases if (!m_pFile->CanUseNativeImage() && g_pConfig->RequireZaps() == EEConfig::REQUIRE_ZAPS_SUPPORTED) return FALSE; #ifdef FEATURE_NATIVE_IMAGE_GENERATION if (IsCompilationProcess()) { // Ignore the assembly being ngened. bool fileIsBeingNGened = false; if (this->GetAppDomain()->IsCompilationDomain()) { Assembly * assemblyBeingNGened = this->GetAppDomain()->ToCompilationDomain()->GetTargetAssembly(); if (assemblyBeingNGened == NULL || assemblyBeingNGened == pDomainAssembly->GetCurrentAssembly()) fileIsBeingNGened = true; } else if (IsSystem()) { // mscorlib gets loaded before the CompilationDomain gets created. // However, we may be ngening mscorlib itself fileIsBeingNGened = true; } if (fileIsBeingNGened) return FALSE; } #endif return TRUE; } void DomainFile::CheckZapRequired() { CONTRACTL { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; if (m_pFile->HasNativeImage() || !IsZapRequired()) return; #ifdef FEATURE_READYTORUN if(m_pFile->GetLoaded()->HasReadyToRunHeader()) return; #endif // Flush any log messages GetFile()->FlushExternalLog(); StackSString ss; ss.Printf("ZapRequire: Could not get native image for %s.\n" "Use FusLogVw.exe to check the reason.", GetSimpleName()); #if defined(_DEBUG) // Assert as some test may not check their error codes well. So throwing an // exception may not cause a test failure (as it should). StackScratchBuffer scratch; DbgAssertDialog(__FILE__, __LINE__, (char*)ss.GetUTF8(scratch)); #endif // defined(_DEBUG) COMPlusThrowNonLocalized(kFileNotFoundException, ss.GetUnicode()); } // Discarding an ngen image can cause problems. For more coverage, // this stress-mode discards ngen images even if not needed. void DomainFile::ClearNativeImageStress() { WRAPPER_NO_CONTRACT; #ifdef _DEBUG static ConfigDWORD clearNativeImageStress; DWORD stressPercentage = clearNativeImageStress.val(CLRConfig::INTERNAL_clearNativeImageStress); _ASSERTE(stressPercentage <= 100); if (stressPercentage == 0 || !GetFile()->HasNativeImage()) return; // Note that discarding a native image can affect dependencies. So its not enough // to only check DomainFile::IsZapRequired() here. if (g_pConfig->RequireZaps() != EEConfig::REQUIRE_ZAPS_NONE) return; // Its OK to ClearNativeImage even for a shared assembly, as the current PEFile will // be discarded if we decide to share the assembly. However, we always use the same // PEFile for the system assembly. So discarding the native image in the current // AppDomain will actually affect the system assembly in the shared domain, and other // appdomains may have already committed to using its ngen image. if (GetFile()->IsSystem() && !this->GetAppDomain()->IsDefaultDomain()) return; if (g_IBCLogger.InstrEnabled()) return; ULONG hash = HashStringA(GetSimpleName()); // Hash in the FileLoadLevel so that we make a different decision for every level. FileLoadLevel fileLoadLevel = m_pDomain->GetDomainFileLoadLevel(this); hash ^= ULONG(fileLoadLevel); // We do not discard native images after this level _ASSERTE(fileLoadLevel < FILE_LOAD_VERIFY_NATIVE_IMAGE_DEPENDENCIES); // Different app-domains should make different decisions hash ^= HashString(this->GetAppDomain()->GetFriendlyName()); #ifdef FEATURE_NATIVE_IMAGE_GENERATION // Since DbgRandomOnHashAndExe() is not so random under ngen.exe, also // factor in the module being compiled if (this->GetAppDomain()->IsCompilationDomain()) { Module * module = this->GetAppDomain()->ToCompilationDomain()->GetTargetModule(); // Has the target module been set yet? if (module) hash ^= HashStringA(module->GetSimpleName()); } #endif if (DbgRandomOnHashAndExe(hash, float(stressPercentage)/100)) { GetFile()->SetCannotUseNativeImage(); GetFile()->ClearNativeImage(); ExternalLog(LL_ERROR, "Rejecting native image for **clearNativeImageStress**"); } #endif } #endif // FEATURE_PREJIT void DomainFile::PreLoadLibrary() { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; } // DomainFile::PreLoadLibrary // Note that this is the sole loading function which must be called OUTSIDE THE LOCK, since // it will potentially involve the OS loader lock. void DomainFile::LoadLibrary() { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; Thread::LoadingFileHolder holder(GetThread()); GetThread()->SetLoadingFile(this); GetFile()->LoadLibrary(); } void DomainFile::PostLoadLibrary() { CONTRACTL { INSTANCE_CHECK; // Note that GetFile()->LoadLibrary must be called before this OUTSIDE OF THE LOCKS PRECONDITION(GetFile()->CheckLoaded()); STANDARD_VM_CHECK; } CONTRACTL_END; #ifdef FEATURE_PREJIT if (GetFile()->HasNativeImage()) { InsertIntoDomainFileWithNativeImageList(); } #endif #ifdef PROFILING_SUPPORTED // After this point, it is possible to load types. // We need to notify the profiler now because the profiler may need to inject methods into // the module, and to do so reliably, it must have the chance to do so before // any types are loaded from the module. // // In the past we only allowed injecting types/methods on non-NGEN images so notifying here // worked ok, but for NGEN images this is pretty ugly. Rejitting often occurs in this callback, // but then during fixup the results of LoadedMethodDesc iterator would change and we would // need to re-iterate everything. Aside from Rejit other code often wasn't designed to handle // running before Fixup. A concrete example VS recently hit, calling GetClassLayout using // a MethodTable which doesn't need restore but its parent pointer isn't fixed up yet. // We've already set the rules so that profilers can't modify the member list of types in NGEN images // so it doesn't matter if types are pre-loaded. We only need the guarantee that code for the // loaded types won't execute yet. For NGEN images we deliver the load notification in // FILE_LOAD_DELIVER_EVENTS. if (!GetFile()->HasNativeImage()) { if (!IsProfilerNotified()) { SetProfilerNotified(); GetCurrentModule()->NotifyProfilerLoadFinished(S_OK); } } #endif } void DomainFile::AddDependencies() { STANDARD_VM_CONTRACT; #ifdef FEATURE_PREJIT // // CoreCLR hard binds to mscorlib.dll only. No need to track hardbound dependencies. // #endif // FEATURE_PREJIT } void DomainFile::EagerFixups() { WRAPPER_NO_CONTRACT; #ifdef FEATURE_PREJIT if (IsIntrospectionOnly()) return; if (GetCurrentModule()->HasNativeImage()) { GetCurrentModule()->RunEagerFixups(); } #ifdef FEATURE_READYTORUN else if (GetCurrentModule()->IsReadyToRun()) { #ifndef CROSSGEN_COMPILE GetCurrentModule()->RunEagerFixups(); #endif PEImageLayout * pLayout = GetCurrentModule()->GetReadyToRunInfo()->GetImage(); TADDR base = dac_cast(pLayout->GetBase()); ExecutionManager::AddCodeRange(base, base + (TADDR)pLayout->GetVirtualSize(), ExecutionManager::GetReadyToRunJitManager(), RangeSection::RANGE_SECTION_READYTORUN, GetCurrentModule() /* (void *)pLayout */); } #endif // FEATURE_READYTORUN #endif // FEATURE_PREJIT } void DomainFile::VtableFixups() { WRAPPER_NO_CONTRACT; } void DomainFile::FinishLoad() { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; #ifdef FEATURE_PREJIT if (m_pFile->HasNativeImage()) { LOG((LF_ZAP, LL_INFO10, "Using native image %S.\n", m_pFile->GetPersistentNativeImage()->GetPath().GetUnicode())); ExternalLog(LL_INFO10, "Native image successfully used."); // Inform metadata that it has been loaded from a native image // (and so there was an opportunity to check for or fix inconsistencies in the original IL metadata) m_pFile->GetMDImport()->SetVerifiedByTrustedSource(TRUE); } // Are we absolutely required to use a native image? CheckZapRequired(); #if defined(FEATURE_COMINTEROP) // If this is a winmd file, ensure that the ngen reference namespace is loadable. // This is necessary as on the phone we don't check ngen image dependencies, and thus we can get in a situation // where a winmd is loaded as a dependency of an ngen image, but the type used to build cross module references // in winmd files isn't loaded. if (GetFile()->AsAssembly()->IsWindowsRuntime() && GetFile()->HasHostAssembly()) { IMDInternalImport *pImport = GetFile()->GetPersistentMDImport(); LPCSTR szNameSpace; LPCSTR szTypeName; // It does not make sense to pass the file name to recieve fake type name for empty WinMDs, because we would use the name // for binding in next call to BindAssemblySpec which would fail for fake WinRT type name // We will throw/return the error instead and the caller will recognize it and react to it by not creating the ngen image - // see code:Zapper::ComputeDependenciesInCurrentDomain if (SUCCEEDED(::GetFirstWinRTTypeDef(pImport, &szNameSpace, &szTypeName, NULL, NULL))) { // Build assembly spec to describe binding to that WinRT type. AssemblySpec spec; IfFailThrow(spec.Init("WindowsRuntimeAssemblyName, ContentType=WindowsRuntime")); spec.SetWindowsRuntimeType(szNameSpace, szTypeName); // Bind to assembly using the CLRPriv binder infrastructure. (All WinRT loads are done through CLRPriv binders ReleaseHolder pAssemblyName; IfFailThrow(spec.CreateFusionName(&pAssemblyName, FALSE, TRUE)); ReleaseHolder pPrivAssembly; IfFailThrow(GetFile()->GetHostAssembly()->BindAssemblyByName(pAssemblyName, &pPrivAssembly)); // Verify that we found this. If this invariant doesn't hold, then the ngen images that reference this winmd are be invalid. // ALSO, this winmd file is invalid as it doesn't follow spec about how it is distributed. if (GetAppDomain()->FindAssembly(pPrivAssembly) != this) { ThrowHR(COR_E_BADIMAGEFORMAT); } } } #endif //defined(FEATURE_COMINTEROP) #endif // FEATURE_PREJIT // Flush any log messages #ifdef FEATURE_PREJIT GetFile()->FlushExternalLog(); #endif // Must set this a bit prematurely for the DAC stuff to work m_level = FILE_LOADED; // Now the DAC can find this module by enumerating assemblies in a domain. DACNotify::DoModuleLoadNotification(m_pModule); #if defined(DEBUGGING_SUPPORTED) && !defined(DACCESS_COMPILE) if (IsDebuggerNotified() && (g_pDebugInterface != NULL)) { // We already notified dbgapi that this module was loading (via LoadModule()). // Now let the dbgapi know the module has reached FILE_LOADED, so it can do any // processing that needs to wait until this stage (e.g., binding breakpoints in // NGENd generics). g_pDebugInterface->LoadModuleFinished(m_pModule, m_pDomain); } #endif // defined(DEBUGGING_SUPPORTED) && !defined(DACCESS_COMPILE) // Set a bit to indicate that the module has been loaded in some domain, and therefore // typeloads can involve types from this module. (Used for candidate instantiations.) GetModule()->SetIsReadyForTypeLoad(); #ifdef FEATURE_PERFMAP // Notify the perfmap of the IL image load. PerfMap::LogImageLoad(m_pFile); #endif } void DomainFile::VerifyExecution() { CONTRACT_VOID { INSTANCE_CHECK; PRECONDITION(IsLoaded()); STANDARD_VM_CHECK; } CONTRACT_END; if (GetModule()->IsIntrospectionOnly()) { // Throw an exception COMPlusThrow(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY); } if(GetFile()->PassiveDomainOnly()) { // Remove path - location must be hidden for security purposes LPCWSTR path=GetFile()->GetPath(); LPCWSTR pStart = wcsrchr(path, '\\'); if (pStart != NULL) pStart++; else pStart = path; COMPlusThrow(kInvalidOperationException, IDS_EE_CODEEXECUTION_ASSEMBLY_FOR_PASSIVE_DOMAIN_ONLY,pStart); } RETURN; } void DomainFile::Activate() { CONTRACT_VOID { INSTANCE_CHECK; PRECONDITION(IsLoaded()); STANDARD_VM_CHECK; } CONTRACT_END; // If we are a module, ensure we've activated the assembly first. if (!IsAssembly()) { GetDomainAssembly()->EnsureActive(); } else { // We cannot execute any code in this assembly until we know what exception plan it is on. // At the point of an exception's stack-crawl it is too late because we cannot tolerate a GC. // See PossiblyUnwrapThrowable and its callers. _ASSERTE(GetLoadedModule() == GetDomainAssembly()->GetLoadedAssembly()->GetManifestModule()); GetLoadedModule()->IsRuntimeWrapExceptions(); } // Now activate any dependencies. // This will typically cause reentrancy of course. if (!IsSingleAppDomain()) { // increment the counter (see the comment in Module::AddActiveDependency) GetModule()->IncrementNumberOfActivations(); #ifdef FEATURE_LOADER_OPTIMIZATION AppDomain *pDomain = this->GetAppDomain(); Module::DependencyIterator i = GetCurrentModule()->IterateActiveDependencies(); STRESS_LOG2(LF_LOADER, LL_INFO100,"Activating module %p in AD %i",GetCurrentModule(),pDomain->GetId().m_dwId); while (i.Next()) { Module *pModule = i.GetDependency(); DomainFile *pDomainFile = pModule->FindDomainFile(pDomain); if (pDomainFile == NULL) pDomainFile = pDomain->LoadDomainNeutralModuleDependency(pModule, FILE_LOADED); STRESS_LOG3(LF_LOADER, LL_INFO100,"Activating dependency %p -> %p, unconditional=%i",GetCurrentModule(),pModule,i.IsUnconditional()); if (i.IsUnconditional()) { // Let any failures propagate pDomainFile->EnsureActive(); } else { // Enable triggers if we fail here if (!pDomainFile->TryEnsureActive()) GetCurrentModule()->EnableModuleFailureTriggers(pModule, this->GetAppDomain()); } STRESS_LOG3(LF_LOADER, LL_INFO100,"Activated dependency %p -> %p, unconditional=%i",GetCurrentModule(),pModule,i.IsUnconditional()); } #endif } #ifndef CROSSGEN_COMPILE if (m_pModule->CanExecuteCode()) { // // Now call the module constructor. Note that this might cause reentrancy; // this is fine and will be handled by the class cctor mechanism. // MethodTable *pMT = m_pModule->GetGlobalMethodTable(); if (pMT != NULL) { pMT->CheckRestore(); m_bDisableActivationCheck=TRUE; pMT->CheckRunClassInitThrowing(); } #ifdef _DEBUG if (g_pConfig->ExpandModulesOnLoad()) { m_pModule->ExpandAll(); } #endif //_DEBUG } else { // This exception does not need to be localized as it can only happen in // NGen and PEVerify, and we are not localizing those tools. _ASSERTE(this->GetAppDomain()->IsPassiveDomain()); // This assert will fire if we attempt to run non-mscorlib code from within ngen // Current audits of the system indicate that this will never occur, but if it does // the exception below will prevent actual non-mscorlib code execution. _ASSERTE(!this->GetAppDomain()->IsCompilationDomain()); LPCWSTR message = W("You may be trying to evaluate a permission from an assembly ") W("without FullTrust, or which cannot execute code for other reasons."); COMPlusThrowNonLocalized(kFileLoadException, message); } #endif // CROSSGEN_COMPILE RETURN; } #ifdef FEATURE_LOADER_OPTIMIZATION BOOL DomainFile::PropagateActivationInAppDomain(Module *pModuleFrom, Module *pModuleTo, AppDomain* pDomain) { CONTRACTL { PRECONDITION(CheckPointer(pModuleFrom)); PRECONDITION(CheckPointer(pModuleTo)); THROWS; // should only throw transient failures DISABLED(GC_TRIGGERS); MODE_ANY; } CONTRACTL_END; #ifdef FEATURE_MULTICOREJIT // Reset the flag to allow managed code to be called in multicore JIT background thread from this routine ThreadStateNCStackHolder holder(-1, Thread::TSNC_CallingManagedCodeDisabled); #endif BOOL completed=true; EX_TRY { GCX_COOP(); ENTER_DOMAIN_PTR(pDomain,ADV_ITERATOR); //iterator DomainFile *pDomainFileFrom = pModuleFrom->FindDomainFile(pDomain); if (pDomainFileFrom != NULL && pDomain->IsLoading(pDomainFileFrom, FILE_ACTIVE)) { STRESS_LOG3(LF_LOADER, LL_INFO100,"Found DomainFile %p for module %p in AppDomain %i\n",pDomainFileFrom,pModuleFrom,pDomain->GetId().m_dwId); DomainFile *pDomainFileTo = pModuleTo->FindDomainFile(pDomain); if (pDomainFileTo == NULL) pDomainFileTo = pDomain->LoadDomainNeutralModuleDependency(pModuleTo, FILE_LOADED); if (!pDomainFileTo->TryEnsureActive()) pModuleFrom->EnableModuleFailureTriggers(pModuleTo, pDomain); else if (!pDomainFileTo->IsActive()) { // We are in a reentrant case completed = FALSE; } } END_DOMAIN_TRANSITION; } EX_CATCH { if (!IsExceptionOfType(kAppDomainUnloadedException, GET_EXCEPTION())) EX_RETHROW; } EX_END_CATCH(SwallowAllExceptions) return completed; } #endif // Returns TRUE if activation is completed for all app domains // static BOOL DomainFile::PropagateNewActivation(Module *pModuleFrom, Module *pModuleTo) { CONTRACTL { PRECONDITION(CheckPointer(pModuleFrom)); PRECONDITION(CheckPointer(pModuleTo)); THROWS; // should only throw transient failures GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; BOOL completed = TRUE; #ifdef FEATURE_LOADER_OPTIMIZATION if (pModuleFrom->GetAssembly()->IsDomainNeutral()) { AppDomainIterator ai(TRUE); Thread *pThread = GetThread(); while (ai.Next()) { STRESS_LOG3(LF_LOADER, LL_INFO100,"Attempting to propagate domain-neutral conditional module dependency %p -> %p to AppDomain %i\n",pModuleFrom,pModuleTo,ai.GetDomain()->GetId().m_dwId); // This is to minimize the chances of trying to run code in an appdomain that's shutting down. if (ai.GetDomain()->CanThreadEnter(pThread)) { completed &= PropagateActivationInAppDomain(pModuleFrom,pModuleTo,ai.GetDomain()); } } } else #endif { AppDomain *pDomain = pModuleFrom->GetDomain()->AsAppDomain(); DomainFile *pDomainFileFrom = pModuleFrom->GetDomainFile(pDomain); if (pDomain->IsLoading(pDomainFileFrom, FILE_ACTIVE)) { // The dependency should already be loaded DomainFile *pDomainFileTo = pModuleTo->GetDomainFile(pDomain); if (!pDomainFileTo->TryEnsureActive()) pModuleFrom->EnableModuleFailureTriggers(pModuleTo, pDomain); else if (!pDomainFileTo->IsActive()) { // Reentrant case completed = FALSE; } } } return completed; } // Checks that module has not been activated in any domain CHECK DomainFile::CheckUnactivatedInAllDomains(Module *pModule) { CONTRACTL { PRECONDITION(CheckPointer(pModule)); THROWS; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; if (pModule->GetAssembly()->IsDomainNeutral()) { AppDomainIterator ai(TRUE); while (ai.Next()) { AppDomain *pDomain = ai.GetDomain(); DomainFile *pDomainFile = pModule->FindDomainFile(pDomain); if (pDomainFile != NULL) CHECK(!pDomainFile->IsActive()); } } else { DomainFile *pDomainFile = pModule->FindDomainFile(pModule->GetDomain()->AsAppDomain()); if (pDomainFile != NULL) CHECK(!pDomainFile->IsActive()); } CHECK_OK; } #ifdef FEATURE_PREJIT DomainFile *DomainFile::FindNextDomainFileWithNativeImage() { LIMITED_METHOD_CONTRACT; return m_pNextDomainFileWithNativeImage; } void DomainFile::InsertIntoDomainFileWithNativeImageList() { LIMITED_METHOD_CONTRACT; while (true) { DomainFile *pLastDomainFileFoundWithNativeImage = m_pDomain->m_pDomainFileWithNativeImageList; m_pNextDomainFileWithNativeImage = pLastDomainFileFoundWithNativeImage; if (pLastDomainFileFoundWithNativeImage == InterlockedCompareExchangeT(&m_pDomain->m_pDomainFileWithNativeImageList, this, pLastDomainFileFoundWithNativeImage)) break; } } #endif //-------------------------------------------------------------------------------- // DomainAssembly //-------------------------------------------------------------------------------- DomainAssembly::DomainAssembly(AppDomain *pDomain, PEFile *pFile, LoaderAllocator *pLoaderAllocator) : DomainFile(pDomain, pFile), m_pAssembly(NULL), m_debuggerFlags(DACF_NONE), m_MissingDependenciesCheckStatus(CMD_Unknown), m_fDebuggerUnloadStarted(FALSE), m_fCollectible(pLoaderAllocator->IsCollectible()), m_fHostAssemblyPublished(false), m_fCalculatedShouldLoadDomainNeutral(false), m_fShouldLoadDomainNeutral(false) { CONTRACTL { CONSTRUCTOR_CHECK; STANDARD_VM_CHECK; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; pFile->ValidateForExecution(); #ifndef CROSSGEN_COMPILE if (m_fCollectible) { ((AssemblyLoaderAllocator *)pLoaderAllocator)->SetDomainAssembly(this); } #endif // !!! backout m_hExposedAssemblyObject = NULL; SetupDebuggingConfig(); // Add a Module iterator entry for this assembly. IfFailThrow(m_Modules.Append(this)); } DomainAssembly::~DomainAssembly() { CONTRACTL { DESTRUCTOR_CHECK; NOTHROW; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; if (m_fHostAssemblyPublished) { // Remove association first. GetAppDomain()->UnPublishHostedAssembly(this); } ModuleIterator i = IterateModules(kModIterIncludeLoading); while (i.Next()) { if (i.GetDomainFile() != this) delete i.GetDomainFile(); } if (m_pAssembly != NULL && !m_pAssembly->IsDomainNeutral()) { delete m_pAssembly; } } void DomainAssembly::ReleaseFiles() { STANDARD_VM_CONTRACT; if(m_pAssembly) m_pAssembly->StartUnload(); ModuleIterator i = IterateModules(kModIterIncludeLoading); while (i.Next()) { if (i.GetDomainFile() != this) i.GetDomainFile()->ReleaseFiles(); } DomainFile::ReleaseFiles(); } void DomainAssembly::SetAssembly(Assembly* pAssembly) { STANDARD_VM_CONTRACT; UpdatePEFile(pAssembly->GetManifestFile()); _ASSERTE(pAssembly->GetManifestModule()->GetFile()==m_pFile); m_pAssembly = pAssembly; m_pModule = pAssembly->GetManifestModule(); pAssembly->SetDomainAssembly(this); } #ifndef CROSSGEN_COMPILE //--------------------------------------------------------------------------------------- // // Returns managed representation of the assembly (Assembly or AssemblyBuilder). // Returns NULL if the managed scout was already collected (see code:LoaderAllocator#AssemblyPhases). // OBJECTREF DomainAssembly::GetExposedAssemblyObject() { CONTRACTL { INSTANCE_CHECK; THROWS; MODE_COOPERATIVE; GC_TRIGGERS; } CONTRACTL_END; LoaderAllocator * pLoaderAllocator = GetLoaderAllocator(); if (!pLoaderAllocator->IsManagedScoutAlive()) { // We already collected the managed scout, so we cannot re-create any managed objects // Note: This is an optimization, as the managed scout can be collected right after this check return NULL; } if (m_hExposedAssemblyObject == NULL) { // Atomically create a handle LOADERHANDLE handle = pLoaderAllocator->AllocateHandle(NULL); FastInterlockCompareExchangePointer(&m_hExposedAssemblyObject, handle, static_cast(NULL)); } if (pLoaderAllocator->GetHandleValue(m_hExposedAssemblyObject) == NULL) { ASSEMBLYREF assemblyObj = NULL; MethodTable * pMT; if (GetFile()->IsDynamic()) { // This is unnecessary because the managed InternalAssemblyBuilder object // should have already been created at the time of DefineDynamicAssembly OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); pMT = MscorlibBinder::GetClass(CLASS__INTERNAL_ASSEMBLY_BUILDER); } else { OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); pMT = MscorlibBinder::GetClass(CLASS__ASSEMBLY); } // Will be TRUE only if LoaderAllocator managed object was already collected and therefore we should // return NULL BOOL fIsLoaderAllocatorCollected = FALSE; // Create the assembly object GCPROTECT_BEGIN(assemblyObj); assemblyObj = (ASSEMBLYREF)AllocateObject(pMT); assemblyObj->SetAssembly(this); // Attach the reference to the assembly to keep the LoaderAllocator for this collectible type // alive as long as a reference to the assembly is kept alive. // Currently we overload the sync root field of the assembly to do so, but the overload is not necessary. if (GetAssembly() != NULL) { OBJECTREF refLA = GetAssembly()->GetLoaderAllocator()->GetExposedObject(); if ((refLA == NULL) && GetAssembly()->GetLoaderAllocator()->IsCollectible()) { // The managed LoaderAllocator object was collected fIsLoaderAllocatorCollected = TRUE; } assemblyObj->SetSyncRoot(refLA); } if (!fIsLoaderAllocatorCollected) { // We should not expose this value in case the LoaderAllocator managed object was already // collected pLoaderAllocator->CompareExchangeValueInHandle(m_hExposedAssemblyObject, (OBJECTREF)assemblyObj, NULL); } GCPROTECT_END(); if (fIsLoaderAllocatorCollected) { // The LoaderAllocator managed object was already collected, we cannot re-create it // Note: We did not publish the allocated Assembly/AssmeblyBuilder object, it will get collected // by GC return NULL; } } return pLoaderAllocator->GetHandleValue(m_hExposedAssemblyObject); } // DomainAssembly::GetExposedAssemblyObject #endif // CROSSGEN_COMPILE #ifdef FEATURE_LOADER_OPTIMIZATION BOOL DomainAssembly::MissingDependenciesCheckDone() { return m_MissingDependenciesCheckStatus != CMD_Unknown; } CMD_State DomainAssembly::CheckMissingDependencies() { //CoreCLR simply doesn't share if dependencies are missing return CMD_NotNeeded; } #endif // FEATURE_LOADER_OPTIMIZATION DomainFile* DomainAssembly::FindIJWModule(HMODULE hMod) { CONTRACT (DomainFile*) { INSTANCE_CHECK; THROWS; GC_NOTRIGGER; MODE_ANY; POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; ModuleIterator i = IterateModules(kModIterIncludeLoaded); while (i.Next()) { PEFile *pFile = i.GetDomainFile()->GetFile(); if ( !pFile->IsResource() && !pFile->IsDynamic() && !pFile->IsILOnly() && pFile->GetIJWBase() == hMod) { RETURN i.GetDomainFile(); } } RETURN NULL; } void DomainAssembly::Begin() { STANDARD_VM_CONTRACT; { AppDomain::LoadLockHolder lock(m_pDomain); m_pDomain->AddAssembly(this); } // Make it possible to find this DomainAssembly object from associated ICLRPrivAssembly. GetAppDomain()->PublishHostedAssembly(this); m_fHostAssemblyPublished = true; } #ifdef FEATURE_PREJIT void DomainAssembly::FindNativeImage() { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; // For non-Apollo builds (i.e., when FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS is // NOT defined), this is how we avoid use of NGEN when diagnostics requests it: By // clearing it out and forcing a load of the MSIL assembly. For Apollo builds // (FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS), though, this doesn't work, as we // don't have MSIL assemblies handy (particularly for Fx Assemblies), so we need to // keep the NGENd image loaded, but to treat it as if it were an MSIL assembly. See // code:PEFile::SetNativeImage. #ifndef FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS if (!NGENImagesAllowed()) { GetFile()->SetCannotUseNativeImage(); if (GetFile()->HasNativeImage()) GetFile()->ClearNativeImage(); return; } #endif // FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS ClearNativeImageStress(); // We already have an image - we just need to do a few more checks if (GetFile()->HasNativeImage()) { #if defined(_DEBUG) if (g_pConfig->ForbidZap(GetSimpleName())) { SString sbuf; StackScratchBuffer scratch; sbuf.Printf("COMPlus_NgenBind_ZapForbid violation: %s.", GetSimpleName()); DbgAssertDialog(__FILE__, __LINE__, sbuf.GetUTF8(scratch)); } #endif ReleaseHolder pNativeImage = GetFile()->GetNativeImageWithRef(); if(!IsSystem() && !SystemDomain::System()->SystemFile()->HasNativeImage() && !CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NgenAllowMscorlibSoftbind)) { m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_MscorlibNotNative; STRESS_LOG2(LF_ZAP,LL_INFO100,"Rejecting native file %p, because mscolib has not NI - reason 0x%x\n",pNativeImage.GetValue(),m_dwReasonForRejectingNativeImage); ExternalLog(LL_ERROR, "Rejecting native image because mscorlib does not have native image"); GetFile()->ClearNativeImage(); // Always throw exceptions when we throw the NI out ThrowHR(CLR_E_BIND_SYS_ASM_NI_MISSING); } else if (!CheckZapSecurity(pNativeImage)) { m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_FailedSecurityCheck; STRESS_LOG2(LF_ZAP,LL_INFO100,"Rejecting native file %p, because security check failed - reason 0x%x\n",pNativeImage.GetValue(),m_dwReasonForRejectingNativeImage); ExternalLog(LL_ERROR, "Rejecting native image because it failed the security check. " "The assembly's permissions must have changed since the time it was ngenned, " "or it is running with a different security context."); GetFile()->ClearNativeImage(); // Always throw exceptions when we throw the NI out ThrowHR(CLR_E_BIND_NI_SECURITY_FAILURE); } else if (!CheckZapDependencyIdentities(pNativeImage)) { m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_DependencyIdentityMismatch; STRESS_LOG2(LF_ZAP,LL_INFO100,"Rejecting native file %p, because dependency identity mismatch - reason 0x%x\n",pNativeImage.GetValue(),m_dwReasonForRejectingNativeImage); ExternalLog(LL_ERROR, "Rejecting native image because of identity mismatch " "with one or more of its assembly dependencies. The assembly needs " "to be ngenned again"); GetFile()->ClearNativeImage(); // Always throw exceptions when we throw the NI out ThrowHR(CLR_E_BIND_NI_DEP_IDENTITY_MISMATCH); } else { // We can only use a native image for a single Module. If this is a domain-bound // load, we know that this means only a single load will use this image, so we can just // flag it as in use. // If on the other hand, we are going to be domain neutral, we may have many loads use // the same native image. Still, we only want to allow the native image to be used // by loads which are going to end up with the same Module. So, we have to effectively // eagerly compute whether that will be the case eagerly, now. To enable this computation, // we store the binding closure in the image. Module * pNativeModule = pNativeImage->GetLoadedLayout()->GetPersistedModuleImage(); EnsureWritablePages(pNativeModule); PEFile ** ppNativeFile = (PEFile **) (PBYTE(pNativeModule) + Module::GetFileOffset()); BOOL bExpectedToBeShared= ShouldLoadDomainNeutral(); if (!bExpectedToBeShared) { GetFile()->SetNativeImageUsedExclusively(); } PEAssembly * pFile = (PEAssembly *)FastInterlockCompareExchangePointer((void **)ppNativeFile, (void *)GetFile(), (void *)NULL); STRESS_LOG3(LF_ZAP,LL_INFO100,"Attempted to set new native file %p, old file was %p, location in the image=%p\n",GetFile(),pFile,ppNativeFile); if (pFile!=NULL && !IsSystem() && ( !bExpectedToBeShared || pFile == PEFile::Dummy() || pFile->IsNativeImageUsedExclusively() || !(GetFile()->GetPath().Equals(pFile->GetPath()))) ) { // The non-shareable native image has already been used in this process by another Module. // We have to abandon the native image. (Note that it isn't enough to // just abandon the preload image, since the code in the file will // reference the image directly). m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_CannotShareNiAssemblyNotDomainNeutral; STRESS_LOG3(LF_ZAP,LL_INFO100,"Rejecting native file %p, because it is already used by file %p - reason 0x%x\n",GetFile(),pFile,m_dwReasonForRejectingNativeImage); ExternalLog(LL_WARNING, "ZAP: An ngen image of an assembly which " "is not loaded as domain-neutral cannot be used in multiple appdomains " "- abandoning ngen image. The assembly will be JIT-compiled in " "the second appdomain. See System.LoaderOptimization.MultiDomain " "for information about domain-neutral loading."); GetFile()->ClearNativeImage(); // We only support a (non-shared) native image to be used from a single // AppDomain. Its not obvious if this is an implementation restriction, // or if this should fail DomainFile::CheckZapRequired(). // We err on the side of conservativeness, so that multi-domain tests // do not blow up in CheckZapRequired() GetFile()->SetCannotUseNativeImage(); } else { //If we are the first and others can reuse us, we cannot go away if ((pFile == NULL) && (!GetFile()->IsNativeImageUsedExclusively())) GetFile()->AddRef(); LOG((LF_ZAP, LL_INFO100, "ZAP: Found a candidate native image for %s\n", GetSimpleName())); } } } if (!GetFile()->HasNativeImage()) { // // Verify that the IL image is consistent with the NGen images loaded into appdomain // AssemblySpec spec; spec.InitializeSpec(GetFile()); GUID mvid; GetFile()->GetMVID(&mvid); GetAppDomain()->CheckForMismatchedNativeImages(&spec, &mvid); } CheckZapRequired(); } #endif // FEATURE_PREJIT BOOL DomainAssembly::ShouldLoadDomainNeutral() { STANDARD_VM_CONTRACT; if (m_fCalculatedShouldLoadDomainNeutral) return m_fShouldLoadDomainNeutral; m_fShouldLoadDomainNeutral = !!ShouldLoadDomainNeutralHelper(); m_fCalculatedShouldLoadDomainNeutral = true; return m_fShouldLoadDomainNeutral; } BOOL DomainAssembly::ShouldLoadDomainNeutralHelper() { STANDARD_VM_CONTRACT; #ifdef FEATURE_LOADER_OPTIMIZATION if (IsSystem()) return TRUE; if (IsSingleAppDomain()) return FALSE; if (GetFile()->IsDynamic()) return FALSE; #ifdef FEATURE_COMINTEROP if (GetFile()->IsWindowsRuntime()) return FALSE; #endif switch(this->GetAppDomain()->GetSharePolicy()) { case AppDomain::SHARE_POLICY_ALWAYS: return TRUE; case AppDomain::SHARE_POLICY_GAC: return IsSystem(); case AppDomain::SHARE_POLICY_NEVER: return FALSE; case AppDomain::SHARE_POLICY_UNSPECIFIED: case AppDomain::SHARE_POLICY_COUNT: break; } return FALSE; // No meaning in doing costly closure walk for CoreCLR. #else // FEATURE_LOADER_OPTIMIZATION return IsSystem(); #endif // FEATURE_LOADER_OPTIMIZATION } // This is where the decision whether an assembly is DomainNeutral (shared) nor not is made. void DomainAssembly::Allocate() { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; AllocMemTracker amTracker; AllocMemTracker * pamTracker = &amTracker; Assembly * pAssembly = m_pAssembly; if (pAssembly==NULL) { //! If you decide to remove "if" do not remove this brace: order is important here - in the case of an exception, //! the Assembly holder must destruct before the AllocMemTracker declared above. NewHolder assemblyHolder(NULL); // Determine whether we are supposed to load the assembly as a shared // assembly or into the app domain. if (ShouldLoadDomainNeutral()) { #ifdef FEATURE_LOADER_OPTIMIZATION // Try to find an existing shared version of the assembly which // is compatible with our domain. SharedDomain * pSharedDomain = SharedDomain::GetDomain(); SIZE_T nInitialShareableAssemblyCount = pSharedDomain->GetShareableAssemblyCount(); DWORD dwSwitchCount = 0; SharedFileLockHolder pFileLock(pSharedDomain, GetFile(), FALSE); if (IsSystem()) { pAssembly=SystemDomain::SystemAssembly(); } else { SharedAssemblyLocator locator(this); pAssembly = pSharedDomain->FindShareableAssembly(&locator); if (pAssembly == NULL) { pFileLock.Acquire(); pAssembly = pSharedDomain->FindShareableAssembly(&locator); } } if (pAssembly == NULL) { // We can now rely on the fact that our MDImport will not change so we can stop refcounting it. GetFile()->MakeMDImportPersistent(); // Go ahead and create new shared version of the assembly if possible // We will need to pass a valid OBJECREF* here in the future when we implement SCU assemblyHolder = pAssembly = Assembly::Create(pSharedDomain, GetFile(), GetDebuggerInfoBits(), FALSE, pamTracker, NULL); if (MissingDependenciesCheckDone()) pAssembly->SetMissingDependenciesCheckDone(); // Compute the closure assembly dependencies // of the code & layout of given assembly. // // An assembly has direct dependencies listed in its manifest. // // We do not in general also have all of those dependencies' dependencies in the manifest. // After all, we may be only using a small portion of the assembly. // // However, since all dependent assemblies must also be shared (so that // the shared data in this assembly can refer to it), we are in // effect forced to behave as though we do have all of their dependencies. // This is because the resulting shared assembly that we will depend on // DOES have those dependencies, but we won't be able to validly share that // assembly unless we match all of ITS dependencies, too. // Sets the tenured bit atomically with the hash insert. pSharedDomain->AddShareableAssembly(pAssembly); } #else // FEATURE_LOADER_OPTIMIZATION _ASSERTE(IsSystem()); if (SystemDomain::SystemAssembly()) { pAssembly = SystemDomain::SystemAssembly(); } else { // We can now rely on the fact that our MDImport will not change so we can stop refcounting it. GetFile()->MakeMDImportPersistent(); // We will need to pass a valid OBJECTREF* here in the future when we implement SCU SharedDomain * pSharedDomain = SharedDomain::GetDomain(); assemblyHolder = pAssembly = Assembly::Create(pSharedDomain, GetFile(), GetDebuggerInfoBits(), FALSE, pamTracker, NULL); pAssembly->SetIsTenured(); } #endif // FEATURE_LOADER_OPTIMIZATION } else { // We can now rely on the fact that our MDImport will not change so we can stop refcounting it. GetFile()->MakeMDImportPersistent(); // We will need to pass a valid OBJECTREF* here in the future when we implement SCU assemblyHolder = pAssembly = Assembly::Create(m_pDomain, GetFile(), GetDebuggerInfoBits(), FALSE, pamTracker, NULL); assemblyHolder->SetIsTenured(); } //@todo! This is too early to be calling SuppressRelease. The right place to call it is below after // the CANNOTTHROWCOMPLUSEXCEPTION. Right now, we have to do this to unblock OOM injection testing quickly // as doing the right thing is nontrivial. pamTracker->SuppressRelease(); assemblyHolder.SuppressRelease(); } #ifdef FEATURE_COMINTEROP // If we are in an AppX process we should prevent loading of PIA in the AppDomain. // This will ensure that we do not run into any compatibility issues in case a type has both a co-Class and a Winrt Class if (AppX::IsAppXProcess() && pAssembly->IsPIA()) { COMPlusThrow(kNotSupportedException, W("NotSupported_PIAInAppxProcess")); } #endif SetAssembly(pAssembly); #ifdef FEATURE_PREJIT BOOL fInsertIntoAssemblySpecBindingCache = TRUE; // Insert AssemblyDef details into AssemblySpecBindingCache if appropriate fInsertIntoAssemblySpecBindingCache = fInsertIntoAssemblySpecBindingCache && GetFile()->CanUseWithBindingCache(); if (fInsertIntoAssemblySpecBindingCache) { AssemblySpec specAssemblyDef; specAssemblyDef.InitializeSpec(GetFile()); if (specAssemblyDef.IsStrongNamed() && specAssemblyDef.HasPublicKey()) { specAssemblyDef.ConvertPublicKeyToToken(); } m_pDomain->AddAssemblyToCache(&specAssemblyDef, this); } #endif } // DomainAssembly::Allocate void DomainAssembly::DeliverAsyncEvents() { CONTRACTL { INSTANCE_CHECK; NOTHROW; GC_TRIGGERS; MODE_ANY; SO_INTOLERANT; } CONTRACTL_END; OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE); m_pDomain->RaiseLoadingAssemblyEvent(this); } void DomainAssembly::DeliverSyncEvents() { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; GetCurrentModule()->NotifyEtwLoadFinished(S_OK); // We may be notified from inside the loader lock if we are delivering IJW events, so keep track. #ifdef PROFILING_SUPPORTED if (!IsProfilerNotified()) { SetProfilerNotified(); GetCurrentModule()->NotifyProfilerLoadFinished(S_OK); } #endif #ifdef DEBUGGING_SUPPORTED GCX_COOP(); if (!IsDebuggerNotified()) { SetShouldNotifyDebugger(); if (m_pDomain->IsDebuggerAttached()) { // If this is the first assembly in the AppDomain, it may be possible to get a better name than the // default. CollectibleAssemblyHolder pDomainAssembly; m_pDomain->m_Assemblies.Get(m_pDomain, 0, pDomainAssembly.This()); if ((pDomainAssembly == this) && !m_pDomain->IsUserCreatedDomain()) m_pDomain->ResetFriendlyName(); } // Still work to do even if no debugger is attached. NotifyDebuggerLoad(ATTACH_ASSEMBLY_LOAD, FALSE); } #endif // DEBUGGING_SUPPORTED } // DomainAssembly::DeliverSyncEvents /* // The enum for dwLocation from managed code: public enum ResourceLocation { Embedded = 1, ContainedInAnotherAssembly = 2, ContainedInManifestFile = 4 } */ BOOL DomainAssembly::GetResource(LPCSTR szName, DWORD *cbResource, PBYTE *pbInMemoryResource, DomainAssembly** pAssemblyRef, LPCSTR *szFileName, DWORD *dwLocation, StackCrawlMark *pStackMark, BOOL fSkipSecurityCheck, BOOL fSkipRaiseResolveEvent) { CONTRACTL { INSTANCE_CHECK; THROWS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; return GetFile()->GetResource( szName, cbResource, pbInMemoryResource, pAssemblyRef, szFileName, dwLocation, pStackMark, fSkipSecurityCheck, fSkipRaiseResolveEvent, this, this->m_pDomain ); } #ifdef FEATURE_PREJIT // -------------------------------------------------------------------------------- // Remember the timestamp of the CLR DLLs used to compile the ngen image. // These will be checked at runtime by PEFile::CheckNativeImageTimeStamp(). // void GetTimeStampsForNativeImage(CORCOMPILE_VERSION_INFO * pNativeVersionInfo) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(::GetAppDomain()->IsCompilationDomain()); } CONTRACTL_END; // Do not store runtime timestamps into NGen image for cross-platform NGen determinism } // // Which processor should ngen target? // This is needed when ngen wants to target for "reach" if the ngen images will be // used on other machines (the Operating System or the OEM build lab can do this). // It can also be used to reduce the testing matrix // void GetNGenCpuInfo(CORINFO_CPU * cpuInfo) { LIMITED_METHOD_CONTRACT; #ifdef _TARGET_X86_ static CORINFO_CPU ngenCpuInfo = { (CPU_X86_PENTIUM_PRO << 8), // dwCPUType 0x00000000, // dwFeatures 0 // dwExtendedFeatures }; // We always generate P3-compatible code on CoreCLR *cpuInfo = ngenCpuInfo; #else // _TARGET_X86_ cpuInfo->dwCPUType = 0; cpuInfo->dwFeatures = 0; cpuInfo->dwExtendedFeatures = 0; #endif // _TARGET_X86_ } // -------------------------------------------------------------------------------- void DomainAssembly::GetCurrentVersionInfo(CORCOMPILE_VERSION_INFO *pNativeVersionInfo) { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; // Clear memory so that we won't write random data into the zapped file ZeroMemory(pNativeVersionInfo, sizeof(CORCOMPILE_VERSION_INFO)); // Pick up any compilation directives for code flavor BOOL fForceDebug, fForceProfiling, fForceInstrument; SystemDomain::GetCompilationOverrides(&fForceDebug, &fForceProfiling, &fForceInstrument); #ifndef FEATURE_PAL pNativeVersionInfo->wOSPlatformID = VER_PLATFORM_WIN32_NT; #else pNativeVersionInfo->wOSPlatformID = VER_PLATFORM_UNIX; #endif // The native images should be OS-version agnostic. Do not store the actual OS version for determinism. // pNativeVersionInfo->wOSMajorVersion = (WORD) osInfo.dwMajorVersion; pNativeVersionInfo->wOSMajorVersion = 4; pNativeVersionInfo->wMachine = IMAGE_FILE_MACHINE_NATIVE_NI; pNativeVersionInfo->wVersionMajor = CLR_MAJOR_VERSION; pNativeVersionInfo->wVersionMinor = CLR_MINOR_VERSION; pNativeVersionInfo->wVersionBuildNumber = CLR_BUILD_VERSION; pNativeVersionInfo->wVersionPrivateBuildNumber = CLR_BUILD_VERSION_QFE; GetNGenCpuInfo(&pNativeVersionInfo->cpuInfo); #if _DEBUG pNativeVersionInfo->wBuild = CORCOMPILE_BUILD_CHECKED; #else pNativeVersionInfo->wBuild = CORCOMPILE_BUILD_FREE; #endif #ifdef DEBUGGING_SUPPORTED if (fForceDebug || !CORDebuggerAllowJITOpts(GetDebuggerInfoBits())) { pNativeVersionInfo->wCodegenFlags |= CORCOMPILE_CODEGEN_DEBUGGING; pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG; } else #endif // DEBUGGING_SUPPORTED { pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG_NONE; } #if defined (PROFILING_SUPPORTED_DATA) || defined(PROFILING_SUPPORTED) if (fForceProfiling || CORProfilerUseProfileImages()) { pNativeVersionInfo->wCodegenFlags |= CORCOMPILE_CODEGEN_PROFILING; pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_PROFILING; #ifdef DEBUGGING_SUPPORTED // Note that we have hardwired profiling to also imply optimized debugging // info. This cuts down on one permutation of prejit files. pNativeVersionInfo->wCodegenFlags &= ~CORCOMPILE_CODEGEN_DEBUGGING; pNativeVersionInfo->wConfigFlags &= ~(CORCOMPILE_CONFIG_DEBUG| CORCOMPILE_CONFIG_DEBUG_DEFAULT); pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG_NONE; #endif // DEBUGGING_SUPPORTED } else #endif // PROFILING_SUPPORTED_DATA || PROFILING_SUPPORTED { pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_PROFILING_NONE; } #ifdef DEBUGGING_SUPPORTED // Note the default assembly flags (from the custom attributes & INI file) , so we can // set determine whether or not the current settings // match the "default" setting or not. // Note that the INI file settings are considered a part of the // assembly, even though they could theoretically change between // ngen time and runtime. It is just too expensive and awkward to // look up the INI file before binding to the native image at // runtime, so we effectively snapshot it at ngen time. DWORD defaultFlags = ComputeDebuggingConfig(); if (CORDebuggerAllowJITOpts(defaultFlags)) { // Default is optimized code if ((pNativeVersionInfo->wCodegenFlags & CORCOMPILE_CODEGEN_DEBUGGING) == 0) pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG_DEFAULT; } else { // Default is non-optimized debuggable code if ((pNativeVersionInfo->wCodegenFlags & CORCOMPILE_CODEGEN_DEBUGGING) != 0) pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG_DEFAULT; } #endif // DEBUGGING_SUPPORTED if (fForceInstrument || GetAssembly()->IsInstrumented()) { pNativeVersionInfo->wCodegenFlags |= CORCOMPILE_CODEGEN_PROF_INSTRUMENTING; pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_INSTRUMENTATION; } else { pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_INSTRUMENTATION_NONE; } GetTimeStampsForNativeImage(pNativeVersionInfo); // Store signature of source assembly. GetOptimizedIdentitySignature(&pNativeVersionInfo->sourceAssembly); // signature will is hash of the whole file. It is written by zapper. // IfFailThrow(CoCreateGuid(&pNativeVersionInfo->signature)); } void DomainAssembly::GetOptimizedIdentitySignature(CORCOMPILE_ASSEMBLY_SIGNATURE *pSignature) { CONTRACTL { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; // // Write the MVID into the version header. // // // If this assembly has skip verification permission, then we store its // mvid. If at load time the assembly still has skip verification // permission, then we can base the matches purely on mvid values and // skip the perf-heavy hashing of the file. // // // The reason that we tell IsFullyTrusted to do a quick check // only is because that allows us make a determination for the most // common full trust scenarios (local machine) without actually // resolving policy and bringing in a whole list of assembly // dependencies. // ReleaseHolder scope (GetFile()->GetMDImportWithRef()); IfFailThrow(scope->GetScopeProps(NULL, &pSignature->mvid)); // Use the NGen image if posssible. IL image does not even have to be present on CoreCLR. if (GetFile()->HasNativeImage()) { PEImageHolder pNativeImage(GetFile()->GetNativeImageWithRef()); CORCOMPILE_VERSION_INFO* pVersionInfo = pNativeImage->GetLoadedLayout()->GetNativeVersionInfo(); pSignature->timeStamp = pVersionInfo->sourceAssembly.timeStamp; pSignature->ilImageSize = pVersionInfo->sourceAssembly.ilImageSize; return; } // Write the time stamp PEImageLayoutHolder ilLayout(GetFile()->GetAnyILWithRef()); pSignature->timeStamp = ilLayout->GetTimeDateStamp(); pSignature->ilImageSize = ilLayout->GetVirtualSize(); } BOOL DomainAssembly::CheckZapDependencyIdentities(PEImage *pNativeImage) { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; AssemblySpec spec; spec.InitializeSpec(this->GetFile()); // The assembly spec should have the binding context associated with it _ASSERTE(spec.GetBindingContext() || spec.IsAssemblySpecForMscorlib()); CORCOMPILE_VERSION_INFO *pVersionInfo = pNativeImage->GetLoadedLayout()->GetNativeVersionInfo(); // Check our own assembly first GetAppDomain()->CheckForMismatchedNativeImages(&spec, &pVersionInfo->sourceAssembly.mvid); // Check MVID in metadata against MVID in CORCOMPILE_VERSION_INFO - important when metadata is loaded from IL instead of NI ReleaseHolder pImport(this->GetFile()->GetMDImportWithRef()); GUID mvid; IfFailThrow(pImport->GetScopeProps(NULL, &mvid)); GetAppDomain()->CheckForMismatchedNativeImages(&spec, &mvid); // Now Check dependencies COUNT_T cDependencies; CORCOMPILE_DEPENDENCY *pDependencies = pNativeImage->GetLoadedLayout()->GetNativeDependencies(&cDependencies); CORCOMPILE_DEPENDENCY *pDependenciesEnd = pDependencies + cDependencies; while (pDependencies < pDependenciesEnd) { if (pDependencies->dwAssemblyDef != mdAssemblyRefNil) { AssemblySpec name; name.InitializeSpec(pDependencies->dwAssemblyDef, pNativeImage->GetNativeMDImport(), this); if (!name.IsAssemblySpecForMscorlib()) { // We just initialized the assembly spec for the NI dependency. This will not have binding context // associated with it, so set it from that of the parent. _ASSERTE(!name.GetBindingContext()); ICLRPrivBinder *pParentAssemblyBindingContext = name.GetBindingContextFromParentAssembly(name.GetAppDomain()); _ASSERTE(pParentAssemblyBindingContext); name.SetBindingContext(pParentAssemblyBindingContext); } GetAppDomain()->CheckForMismatchedNativeImages(&name, &pDependencies->signAssemblyDef.mvid); } pDependencies++; } return TRUE; } BOOL DomainAssembly::CheckZapSecurity(PEImage *pNativeImage) { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; return TRUE; } #endif // FEATURE_PREJIT // @todo Find a better place for these #define DE_CUSTOM_VALUE_NAMESPACE "System.Diagnostics" #define DE_DEBUGGABLE_ATTRIBUTE_NAME "DebuggableAttribute" // @todo .INI file is a temporary workaround for Beta 1 #define DE_INI_FILE_SECTION_NAME W(".NET Framework Debugging Control") #define DE_INI_FILE_KEY_TRACK_INFO W("GenerateTrackingInfo") #define DE_INI_FILE_KEY_ALLOW_JIT_OPTS W("AllowOptimize") DWORD DomainAssembly::ComputeDebuggingConfig() { CONTRACTL { INSTANCE_CHECK; THROWS; WRAPPER(GC_TRIGGERS); MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; #ifdef DEBUGGING_SUPPORTED DWORD dacfFlags = DACF_ALLOW_JIT_OPTS; if (GetDebuggingOverrides(&dacfFlags)) { dacfFlags |= DACF_USER_OVERRIDE; } else { IfFailThrow(GetDebuggingCustomAttributes(&dacfFlags)); } return dacfFlags; #else // !DEBUGGING_SUPPORTED return 0; #endif // DEBUGGING_SUPPORTED } void DomainAssembly::SetupDebuggingConfig(void) { CONTRACTL { INSTANCE_CHECK; THROWS; WRAPPER(GC_TRIGGERS); MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; #ifdef DEBUGGING_SUPPORTED DWORD dacfFlags = ComputeDebuggingConfig(); SetDebuggerInfoBits((DebuggerAssemblyControlFlags)dacfFlags); LOG((LF_CORDB, LL_INFO10, "Assembly %S: bits=0x%x\n", GetDebugName(), GetDebuggerInfoBits())); #endif // DEBUGGING_SUPPORTED } // The format for the (temporary) .INI file is: // [.NET Framework Debugging Control] // GenerateTrackingInfo= where n is 0 or 1 // AllowOptimize= where n is 0 or 1 // Where neither x nor y equal INVALID_INI_INT: #define INVALID_INI_INT (0xFFFF) bool DomainAssembly::GetDebuggingOverrides(DWORD *pdwFlags) { CONTRACTL { INSTANCE_CHECK; THROWS; GC_NOTRIGGER; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; #if defined(DEBUGGING_SUPPORTED) && !defined(FEATURE_CORESYSTEM) // TODO FIX in V5.0 // Any touch of the file system is relatively expensive even in the warm case. // // Ideally we remove the .INI feature completely (if we need something put it in the .exe.config file) // // However because of compatibility concerns, we won't do this until the next side-by-side release // In the mean time don't check in the case where we have already loaded the NGEN image, as the // JIT overrides don't mean anything in that case as we won't be jitting anyway. // This avoids doing these probes for framework DLLs right away. if (GetFile()->HasNativeImage()) return false; _ASSERTE(pdwFlags); bool fHasBits = false; WCHAR *pFileName = NULL; HRESULT hr = S_OK; UINT cbExtOrValue = 4; WCHAR *pTail = NULL; size_t len = 0; WCHAR *lpFileName = NULL; const WCHAR *wszFileName = GetFile()->GetPath(); if (wszFileName == NULL) { return false; } // lpFileName is a copy of the original, and will be edited. CQuickBytes qb; len = wcslen(wszFileName); size_t cchlpFileName = (len + 1); lpFileName = (WCHAR*)qb.AllocThrows(cchlpFileName * sizeof(WCHAR)); wcscpy_s(lpFileName, cchlpFileName, wszFileName); pFileName = wcsrchr(lpFileName, W('\\')); if (pFileName == NULL) { pFileName = lpFileName; } if (*pFileName == W('\\')) { pFileName++; //move the pointer past the last '\' } _ASSERTE(wcslen(W(".INI")) == cbExtOrValue); if (pFileName == NULL || (pTail=wcsrchr(pFileName, W('.'))) == NULL || (wcslen(pTail) pNativeImage=GetFile()->GetNativeImageWithRef(); if (pNativeImage) { CORCOMPILE_VERSION_INFO * pVersion = pNativeImage->GetLoadedLayout()->GetNativeVersionInfo(); PREFIX_ASSUME(pVersion != NULL); WORD codegen = pVersion->wCodegenFlags; if (codegen & CORCOMPILE_CODEGEN_DEBUGGING) { *pdwFlags &= (~DACF_ALLOW_JIT_OPTS); } else { *pdwFlags |= DACF_ALLOW_JIT_OPTS; } } else #endif // FEATURE_PREJIT { ULONG size; BYTE *blob; mdModule mdMod; ReleaseHolder mdImport(GetFile()->GetMDImportWithRef()); mdMod = mdImport->GetModuleFromScope(); mdAssembly asTK = TokenFromRid(mdtAssembly, 1); hr = mdImport->GetCustomAttributeByName(asTK, DE_CUSTOM_VALUE_NAMESPACE NAMESPACE_SEPARATOR_STR DE_DEBUGGABLE_ATTRIBUTE_NAME, (const void**)&blob, &size); // If there is no custom value, then there is no entrypoint defined. if (!(FAILED(hr) || hr == S_FALSE)) { // We're expecting a 6 or 8 byte blob: // // 1, 0, enable tracking, disable opts, 0, 0 if ((size == 6) || (size == 8)) { if (!((blob[0] == 1) && (blob[1] == 0))) { BAD_FORMAT_NOTHROW_ASSERT(!"Invalid blob format for custom attribute"); return COR_E_BADIMAGEFORMAT; } if (blob[2] & 0x1) { *pdwFlags |= DACF_OBSOLETE_TRACK_JIT_INFO; } else { *pdwFlags &= (~DACF_OBSOLETE_TRACK_JIT_INFO); } if (blob[2] & 0x2) { *pdwFlags |= DACF_IGNORE_PDBS; } else { *pdwFlags &= (~DACF_IGNORE_PDBS); } // For compatibility, we enable optimizations if the tracking byte is zero, // even if disable opts is nonzero if (((blob[2] & 0x1) == 0) || (blob[3] == 0)) { *pdwFlags |= DACF_ALLOW_JIT_OPTS; } else { *pdwFlags &= (~DACF_ALLOW_JIT_OPTS); } LOG((LF_CORDB, LL_INFO10, "Assembly %S: has %s=%d,%d bits = 0x%x\n", GetDebugName(), DE_DEBUGGABLE_ATTRIBUTE_NAME, blob[2], blob[3], *pdwFlags)); } } } return hr; } BOOL DomainAssembly::NotifyDebuggerLoad(int flags, BOOL attaching) { WRAPPER_NO_CONTRACT; BOOL result = FALSE; if (!IsVisibleToDebugger()) return FALSE; // Debugger Attach is done totally out-of-process. Does not call code in-proc. _ASSERTE(!attaching); // Make sure the debugger has been initialized. See code:Debugger::Startup. if (g_pDebugInterface == NULL) { _ASSERTE(!CORDebuggerAttached()); return FALSE; } // There is still work we need to do even when no debugger is attached. if (flags & ATTACH_ASSEMBLY_LOAD) { if (ShouldNotifyDebugger()) { g_pDebugInterface->LoadAssembly(this); } result = TRUE; } DomainModuleIterator i = IterateModules(kModIterIncludeLoading); while (i.Next()) { DomainFile * pDomainFile = i.GetDomainFile(); if(pDomainFile->ShouldNotifyDebugger()) { result = result || pDomainFile->GetModule()->NotifyDebuggerLoad(this->GetAppDomain(), pDomainFile, flags, attaching); } } if( ShouldNotifyDebugger()) { result|=m_pModule->NotifyDebuggerLoad(m_pDomain, this, ATTACH_MODULE_LOAD, attaching); SetDebuggerNotified(); } return result; } void DomainAssembly::NotifyDebuggerUnload() { LIMITED_METHOD_CONTRACT; if (!IsVisibleToDebugger()) return; if (!this->GetAppDomain()->IsDebuggerAttached()) return; m_fDebuggerUnloadStarted = TRUE; // Dispatch module unloads for all modules. Debugger is resilient in case we haven't dispatched // a previous load event (such as if debugger attached after the modules was loaded). DomainModuleIterator i = IterateModules(kModIterIncludeLoading); while (i.Next()) { i.GetDomainFile()->GetModule()->NotifyDebuggerUnload(this->GetAppDomain()); } g_pDebugInterface->UnloadAssembly(this); } // This will enumerate for static GC refs (but not thread static GC refs) void DomainAssembly::EnumStaticGCRefs(promote_func* fn, ScanContext* sc) { CONTRACT_VOID { NOTHROW; GC_NOTRIGGER; } CONTRACT_END; _ASSERTE(GCHeapUtilities::IsGCInProgress() && GCHeapUtilities::IsServerHeap() && IsGCSpecialThread()); DomainModuleIterator i = IterateModules(kModIterIncludeLoaded); while (i.Next()) { DomainFile* pDomainFile = i.GetDomainFile(); if (pDomainFile->IsActive()) { // We guarantee that at this point the module has it's DomainLocalModule set up // , as we create it while we load the module _ASSERTE(pDomainFile->GetLoadedModule()->GetDomainLocalModule(this->GetAppDomain())); pDomainFile->GetLoadedModule()->EnumRegularStaticGCRefs(this->GetAppDomain(), fn, sc); // We current to do not iterate over the ThreadLocalModules that correspond // to this Module. The GC discovers thread statics through the handle table. } } RETURN; } #endif // #ifndef DACCESS_COMPILE #ifdef DACCESS_COMPILE void DomainFile::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) { SUPPORTS_DAC; //sizeof(DomainFile) == 0x60 DAC_ENUM_VTHIS(); // Modules are needed for all minidumps, but they are enumerated elsewhere // so we don't need to duplicate effort; thus we do noting with m_pModule. // For MiniDumpNormal, we only want the file name. if (m_pFile.IsValid()) { m_pFile->EnumMemoryRegions(flags); } if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && m_pDomain.IsValid()) { m_pDomain->EnumMemoryRegions(flags, true); } } void DomainAssembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) { SUPPORTS_DAC; //sizeof(DomainAssembly) == 0xe0 DAC_ENUM_VTHIS(); DomainFile::EnumMemoryRegions(flags); // For minidumps without full memory, we need to always be able to iterate over m_Modules. m_Modules.EnumMemoryRegions(flags); if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) { if (m_pAssembly.IsValid()) { m_pAssembly->EnumMemoryRegions(flags); } } } #endif // #ifdef DACCESS_COMPILE