From ba28992b5723ed5bee39829876a39b9b411594c9 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Mon, 7 Oct 2024 22:25:55 +0300 Subject: [PATCH] Move more helpers from DomainAssembly to Assembly (#108568) --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 26 +- src/coreclr/debug/ee/debugger.cpp | 12 +- src/coreclr/vm/appdomain.cpp | 4 +- src/coreclr/vm/appdomain.hpp | 4 +- src/coreclr/vm/assembly.cpp | 595 ++++++++++++++++++++- src/coreclr/vm/assembly.hpp | 28 +- src/coreclr/vm/assemblyspec.hpp | 37 +- src/coreclr/vm/ceeload.cpp | 6 +- src/coreclr/vm/domainassembly.cpp | 641 +---------------------- src/coreclr/vm/domainassembly.h | 120 +---- src/coreclr/vm/eventtrace.cpp | 2 +- src/coreclr/vm/loaderallocator.cpp | 4 +- src/coreclr/vm/loaderallocator.inl | 2 +- 13 files changed, 668 insertions(+), 813 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 770e81a9480e1..4788dd322d2e1 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -672,7 +672,7 @@ void DacDbiInterfaceImpl::GetCompilerFlags ( } // Get the underlying module - none of this is AppDomain specific - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); DWORD dwBits = pModule->GetDebuggerInfoBits(); *pfAllowJITOpts = !CORDisableJITOptimizations(dwBits); *pfEnableEnC = pModule->IsEditAndContinueEnabled(); @@ -721,7 +721,7 @@ HRESULT DacDbiInterfaceImpl::SetCompilerFlags(VMPTR_DomainAssembly vmDomainAssem DWORD dwBits = 0; DomainAssembly * pDomainAssembly = vmDomainAssembly.GetDacPtr(); - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); HRESULT hr = S_OK; @@ -821,7 +821,7 @@ SIZE_T DacDbiInterfaceImpl::GetArgCount(MethodDesc * pMD) return 0; } - MetaSig msig(pCallSig, cbCallSigSize, pMD->GetModule(), NULL, MetaSig::sigMember); + MetaSig msig(pCallSig, cbCallSigSize, pMD->GetAssembly()->GetModule(), NULL, MetaSig::sigMember); // Get the arg count. UINT32 NumArguments = msig.NumFixedArgs(); @@ -1019,7 +1019,7 @@ void DacDbiInterfaceImpl::GetSequencePoints(MethodDesc * pMethodDesc, #endif // if there is a profiler load-time mapping and not a rejit mapping, apply that instead InstrumentedILOffsetMapping loadTimeMapping = - pMethodDesc->GetModule()->GetInstrumentedILOffsetMapping(pMethodDesc->GetMemberDef()); + pMethodDesc->GetAssembly()->GetModule()->GetInstrumentedILOffsetMapping(pMethodDesc->GetMemberDef()); ComposeMapping(&loadTimeMapping, mapCopy, &entryCount); #ifdef FEATURE_REJIT } @@ -1091,7 +1091,7 @@ void DacDbiInterfaceImpl::GetILCodeAndSig(VMPTR_DomainAssembly vmDomainAssembly, DD_ENTER_MAY_THROW; DomainAssembly * pDomainAssembly = vmDomainAssembly.GetDacPtr(); - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); RVA methodRVA = 0; DWORD implFlags; @@ -1330,7 +1330,7 @@ void DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_DomainAssembly vmDomai pCodeInfo->Clear(); DomainAssembly * pDomainAssembly = vmDomainAssembly.GetDacPtr(); - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); @@ -2866,7 +2866,7 @@ TypeHandle DacDbiInterfaceImpl::GetClassOrValueTypeHandle(DebuggerIPCE_BasicType else { DomainAssembly * pDomainAssembly = pData->vmDomainAssembly.GetDacPtr(); - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); typeHandle = ClassLoader::LookupTypeDefOrRefInModule(pModule, pData->metadataToken); if (typeHandle.IsNull()) @@ -3842,7 +3842,7 @@ FieldDesc * DacDbiInterfaceImpl::GetEnCFieldDesc(const EnCHangingFieldInfo * pEn FieldDesc * pFD = NULL; DomainAssembly * pDomainAssembly = pEnCFieldInfo->GetObjectTypeData().vmDomainAssembly.GetDacPtr(); - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); // get the type handle for the object TypeHandle typeHandle = ClassLoader::LookupTypeDefOrRefInModule(pModule, @@ -3881,7 +3881,7 @@ PTR_CBYTE DacDbiInterfaceImpl::GetPtrToEnCField(FieldDesc * pFD, const EnCHangin PTR_EditAndContinueModule pEnCModule; DomainAssembly * pDomainAssembly = pEnCFieldInfo->GetObjectTypeData().vmDomainAssembly.GetDacPtr(); - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); // make sure we actually have an EditAndContinueModule _ASSERTE(pModule->IsEditAndContinueCapable()); @@ -4053,7 +4053,7 @@ void DacDbiInterfaceImpl::ResolveTypeReference(const TypeRefData * pTypeRefInfo, { DD_ENTER_MAY_THROW; DomainAssembly * pDomainAssembly = pTypeRefInfo->vmDomainAssembly.GetDacPtr(); - Module * pReferencingModule = pDomainAssembly->GetModule(); + Module * pReferencingModule = pDomainAssembly->GetAssembly()->GetModule(); BOOL fSuccess = FALSE; // Resolve the type ref @@ -4317,7 +4317,7 @@ void DacDbiInterfaceImpl::GetModuleForDomainAssembly(VMPTR_DomainAssembly vmDoma _ASSERTE(pModule != NULL); DomainAssembly * pDomainAssembly = vmDomainAssembly.GetDacPtr(); - pModule->SetHostPtr(pDomainAssembly->GetModule()); + pModule->SetHostPtr(pDomainAssembly->GetAssembly()->GetModule()); } @@ -4446,7 +4446,7 @@ void DacDbiInterfaceImpl::EnumerateModulesInAssembly( DomainAssembly * pDomainAssembly = vmAssembly.GetDacPtr(); // Debugger isn't notified of Resource / Inspection-only modules. - if (pDomainAssembly->GetModule()->IsVisibleToDebugger()) + if (pDomainAssembly->GetAssembly()->GetModule()->IsVisibleToDebugger()) { // If domain assembly isn't yet loaded, just return if (!pDomainAssembly->GetAssembly()->IsLoaded()) @@ -4469,7 +4469,7 @@ VMPTR_DomainAssembly DacDbiInterfaceImpl::ResolveAssembly( DomainAssembly * pDomainAssembly = vmScope.GetDacPtr(); - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); VMPTR_DomainAssembly vmDomainAssembly = VMPTR_DomainAssembly::NullPtr(); diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 9159080ded6dd..7015d6010f166 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -5134,8 +5134,8 @@ DebuggerModule * Debugger::LookupOrCreateModule(DomainAssembly * pDomainAssembly { _ASSERTE(pDomainAssembly != NULL); LOG((LF_CORDB, LL_INFO1000, "D::LOCM df=%p\n", pDomainAssembly)); - DebuggerModule * pDModule = LookupOrCreateModule(pDomainAssembly->GetModule()); - LOG((LF_CORDB, LL_INFO1000, "D::LOCM m=%p ad=%p -> dm=%p\n", pDomainAssembly->GetModule(), AppDomain::GetCurrentDomain(), pDModule)); + DebuggerModule * pDModule = LookupOrCreateModule(pDomainAssembly->GetAssembly()->GetModule()); + LOG((LF_CORDB, LL_INFO1000, "D::LOCM m=%p ad=%p -> dm=%p\n", pDomainAssembly->GetAssembly()->GetModule(), AppDomain::GetCurrentDomain(), pDModule)); _ASSERTE(pDModule != NULL); _ASSERTE(pDModule->GetDomainAssembly() == pDomainAssembly); @@ -5235,7 +5235,7 @@ DebuggerModule* Debugger::AddDebuggerModule(DomainAssembly * pDomainAssembly) LOG((LF_CORDB, LL_INFO1000, "D::ADM df=0x%x\n", pDomainAssembly)); DebuggerDataLockHolder chInfo(this); - Module * pRuntimeModule = pDomainAssembly->GetModule(); + Module * pRuntimeModule = pDomainAssembly->GetAssembly()->GetModule(); HRESULT hr = CheckInitModuleTable(); IfFailThrow(hr); @@ -8041,7 +8041,7 @@ BOOL Debugger::ShouldSendCustomNotification(DomainAssembly *pAssembly, mdTypeDef } CONTRACTL_END; - Module *pModule = pAssembly->GetModule(); + Module *pModule = pAssembly->GetAssembly()->GetModule(); TypeInModule tim(pModule, typeDef); return !(m_pCustomNotificationTable->Lookup(tim).IsNull()); } @@ -9402,7 +9402,7 @@ void Debugger::LoadModule(Module* pRuntimeModule, // We should simply things when we actually get rid of DebuggerModule, possibly by just passing the // DomainAssembly around. _ASSERTE(module->GetDomainAssembly() == pDomainAssembly); - _ASSERTE(module->GetRuntimeModule() == pDomainAssembly->GetModule()); + _ASSERTE(module->GetRuntimeModule() == pDomainAssembly->GetAssembly()->GetModule()); // Send a load module event to the Right Side. ipce = m_pRCThread->GetIPCEventSendBuffer(); @@ -10830,7 +10830,7 @@ bool Debugger::HandleIPCEvent(DebuggerIPCEvent * pEvent) // unexpected in an OOM situation. Quickly just sanity check them. // Thread * pThread = pEvent->SetIP.vmThreadToken.GetRawPtr(); - Module * pModule = pEvent->SetIP.vmDomainAssembly.GetRawPtr()->GetModule(); + Module * pModule = pEvent->SetIP.vmDomainAssembly.GetRawPtr()->GetAssembly()->GetModule(); // Get the DJI for this function DebuggerMethodInfo * pDMI = GetOrCreateMethodInfo(pModule, pEvent->SetIP.mdMethod); diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 34f1f6b0dea8a..ce933c43739a1 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -2472,7 +2472,7 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity, // Set the assembly module to be tenured now that we know it won't be deleted pDomainAssembly->GetAssembly()->SetIsTenured(); - if (pDomainAssembly->IsCollectible()) + if (pDomainAssembly->GetAssembly()->IsCollectible()) { // We add the assembly to the LoaderAllocator only when we are sure that it can be added // and won't be deleted in case of a concurrent load from the same ALC @@ -4427,7 +4427,7 @@ AppDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, bool enumThis) while (assem.Next(pAssembly.This())) { - pAssembly->GetDomainAssembly()->EnumMemoryRegions(flags); + pAssembly->EnumMemoryRegions(flags); } } diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 248d37f203a10..b5938414511b6 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -544,12 +544,12 @@ class CollectibleAssemblyHolderBase LoaderAllocator * GetLoaderAllocator(DomainAssembly * pDomainAssembly) { WRAPPER_NO_CONTRACT; - return pDomainAssembly->GetLoaderAllocator(); + return pDomainAssembly->GetAssembly()->GetLoaderAllocator(); } BOOL IsCollectible(DomainAssembly * pDomainAssembly) { WRAPPER_NO_CONTRACT; - return pDomainAssembly->IsCollectible(); + return pDomainAssembly->GetAssembly()->IsCollectible(); } LoaderAllocator * GetLoaderAllocator(Assembly * pAssembly) { diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index 9c2b72036df4c..6309f9bfcb10c 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -142,6 +142,7 @@ Assembly::Assembly(PEAssembly* pPEAssembly, LoaderAllocator *pLoaderAllocator) #endif , m_debuggerFlags{DACF_NONE} , m_hExposedObject{} + , m_NextAssemblyInSameALC(NULL) { CONTRACTL { @@ -490,7 +491,7 @@ Assembly *Assembly::CreateDynamic(AssemblyBinder* pBinder, NativeAssemblyNamePar pDomainAssembly = new DomainAssembly(pPEAssembly, pLoaderAllocator, pamTracker); pAssem = pDomainAssembly->GetAssembly(); pAssem->m_isDynamic = true; - if (pDomainAssembly->IsCollectible()) + if (pAssem->IsCollectible()) { // We add the assembly to the LoaderAllocator only when we are sure that it can be added // and won't be deleted in case of a concurrent load from the same ALC @@ -1936,9 +1937,596 @@ Assembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) } } +#else // DACCESS_COMPILE + +// Optimization intended for EnsureLoadLevel only +#include +void Assembly::EnsureLoadLevel(FileLoadLevel targetLevel) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + THROWS; + GC_TRIGGERS; + } + CONTRACT_END; + + TRIGGERSGC (); + if (IsLoading()) + { + AppDomain::GetCurrentDomain()->LoadAssembly(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 constraints.) + + RequireLoadLevel((FileLoadLevel)(targetLevel-1)); + } + else + ThrowIfError(targetLevel); + + RETURN; +} + +#include +CHECK Assembly::CheckLoadLevel(FileLoadLevel requiredLevel, BOOL deadlockOK) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + if (deadlockOK) + { + // 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(AppDomain::GetCurrentDomain()->CheckLoading(this, requiredLevel)); + } + else + { + CHECK_MSG(m_level >= requiredLevel, + "File not sufficiently loaded"); + } + + CHECK_OK; +} + +void Assembly::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 Assembly::SetError(Exception *ex) +{ + CONTRACT_VOID + { + PRECONDITION(!IsError()); + PRECONDITION(ex != NULL); + INSTANCE_CHECK; + THROWS; + GC_TRIGGERS; + POSTCONDITION(IsError()); + } + CONTRACT_END; + + m_pError = ex->DomainBoundClone(); + + if (m_pModule) + { + m_pModule->NotifyEtwLoadFinished(ex->GetHR()); + + if (!IsProfilerNotified()) + { + SetProfilerNotified(); + +#ifdef PROFILING_SUPPORTED + // Only send errors for non-shared assemblies; other assemblies might be successfully completed + // in another app domain later. + m_pModule->NotifyProfilerLoadFinished(ex->GetHR()); #endif + } + } -#ifndef DACCESS_COMPILE + RETURN; +} + +void Assembly::ThrowIfError(FileLoadLevel targetLevel) +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + MODE_ANY; + THROWS; + GC_TRIGGERS; + } + CONTRACT_END; + + if (m_level < targetLevel && m_pError != NULL) + { + PAL_CPP_THROW(Exception*, m_pError->DomainBoundClone()); + } + + RETURN; +} + +CHECK Assembly::CheckNoError(FileLoadLevel targetLevel) +{ + LIMITED_METHOD_CONTRACT; + CHECK(m_level >= targetLevel + || !IsError()); + + CHECK_OK; +} + +CHECK Assembly::CheckActivated() +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + CHECK_MSG(CheckNoError(FILE_ACTIVE), "Assembly load resulted in an error"); + + if (IsActive()) + CHECK_OK; + + // CoreLib 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 (GetPEAssembly()->IsSystem()) + CHECK_OK; + + CHECK_MSG(GetPEAssembly()->IsLoaded(), "PEAssembly has not been loaded"); + CHECK_MSG(IsLoaded(), "Assembly has not been fully loaded"); +#ifdef _DEBUG + CHECK_MSG(m_bDisableActivationCheck || CheckLoadLevel(FILE_ACTIVE), "File has not had execution verified"); +#endif + CHECK_OK; +} + +BOOL Assembly::DoIncrementalLoad(FileLoadLevel level) +{ + STANDARD_VM_CONTRACT; + + if (IsError()) + return FALSE; + + switch (level) + { + case FILE_LOAD_BEGIN: + Begin(); + break; + + case FILE_LOAD_BEFORE_TYPE_LOAD: + BeforeTypeLoad(); + break; + + case FILE_LOAD_EAGER_FIXUPS: + EagerFixups(); + break; + + case FILE_LOAD_DELIVER_EVENTS: + DeliverSyncEvents(); + break; + + case FILE_LOAD_VTABLE_FIXUPS: + VtableFixups(); + break; + + case FILE_LOADED: + FinishLoad(); + break; + + case FILE_ACTIVE: + Activate(); + break; + + default: + UNREACHABLE(); + } + +#ifdef FEATURE_MULTICOREJIT + { + Module * pModule = m_pModule; + + if (pModule != NULL) // Should not triggle assert when module is NULL + { + AppDomain::GetCurrentDomain()->GetMulticoreJitManager().RecordModuleLoad(pModule, level); + } + } +#endif + + return TRUE; +} + +void Assembly::BeforeTypeLoad() +{ + CONTRACTL + { + INSTANCE_CHECK; + // Note that GetPEAssembly()->EnsureLoaded must be called before this OUTSIDE OF THE LOCKS + PRECONDITION(GetPEAssembly()->IsLoaded()); + STANDARD_VM_CHECK; + } + CONTRACTL_END; + +#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 (!IsProfilerNotified()) + { + SetProfilerNotified(); + GetModule()->NotifyProfilerLoadFinished(S_OK); + } + +#endif +} + +void Assembly::EagerFixups() +{ + WRAPPER_NO_CONTRACT; + +#ifdef FEATURE_READYTORUN + if (GetModule()->IsReadyToRun()) + { + GetModule()->RunEagerFixups(); + + } +#endif // FEATURE_READYTORUN +} + +void Assembly::VtableFixups() +{ + WRAPPER_NO_CONTRACT; + + GetModule()->FixupVTables(); +} + +void Assembly::FinishLoad() +{ + CONTRACTL + { + INSTANCE_CHECK; + STANDARD_VM_CHECK; + } + CONTRACTL_END; + + // 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); +} + +void Assembly::Activate() +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(IsLoaded()); + STANDARD_VM_CHECK; + } + CONTRACT_END; + + // 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. + GetModule()->IsRuntimeWrapExceptions(); + + // + // 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(); +#ifdef _DEBUG + m_bDisableActivationCheck=TRUE; +#endif + pMT->CheckRunClassInitThrowing(); + } +#ifdef _DEBUG + if (g_pConfig->ExpandModulesOnLoad()) + { + m_pModule->ExpandAll(); + } +#endif //_DEBUG + +#ifdef FEATURE_READYTORUN + if (m_pModule->IsReadyToRun()) + { + m_pModule->GetReadyToRunInfo()->RegisterUnrelatedR2RModule(); + } +#endif + + RETURN; +} + +void Assembly::Begin() +{ + STANDARD_VM_CONTRACT; + + { + AppDomain::LoadLockHolder lock(AppDomain::GetCurrentDomain()); + AppDomain::GetCurrentDomain()->AddAssembly(GetDomainAssembly()); + } + // Make it possible to find this DomainAssembly object from associated BINDER_SPACE::Assembly. + RegisterWithHostAssembly(); +} + +void Assembly::RegisterWithHostAssembly() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END + + if (GetPEAssembly()->HasHostAssembly()) + { + GetPEAssembly()->GetHostAssembly()->SetRuntimeAssembly(this); + } +} + +void Assembly::UnregisterFromHostAssembly() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END + + if (GetPEAssembly()->HasHostAssembly()) + { + GetPEAssembly()->GetHostAssembly()->SetRuntimeAssembly(nullptr); + } +} + +void Assembly::DeliverAsyncEvents() +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE); + AppDomain::GetCurrentDomain()->RaiseLoadingAssemblyEvent(this); +} + +void Assembly::DeliverSyncEvents() +{ + CONTRACTL + { + INSTANCE_CHECK; + STANDARD_VM_CHECK; + } + CONTRACTL_END; + + GetModule()->NotifyEtwLoadFinished(S_OK); + +#ifdef PROFILING_SUPPORTED + if (!IsProfilerNotified()) + { + SetProfilerNotified(); + GetModule()->NotifyProfilerLoadFinished(S_OK); + } +#endif + +#ifdef DEBUGGING_SUPPORTED + GCX_COOP(); + if (!IsDebuggerNotified()) + { + SetShouldNotifyDebugger(); + + // Still work to do even if no debugger is attached. + NotifyDebuggerLoad(ATTACH_ASSEMBLY_LOAD, FALSE); + + } +#endif // DEBUGGING_SUPPORTED +} + +DebuggerAssemblyControlFlags Assembly::ComputeDebuggingConfig() +{ + CONTRACTL + { + INSTANCE_CHECK; + THROWS; + WRAPPER(GC_TRIGGERS); + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + +#ifdef DEBUGGING_SUPPORTED + DWORD dacfFlags = DACF_ALLOW_JIT_OPTS; + IfFailThrow(GetDebuggingCustomAttributes(&dacfFlags)); + return (DebuggerAssemblyControlFlags)dacfFlags; +#else // !DEBUGGING_SUPPORTED + return 0; +#endif // DEBUGGING_SUPPORTED +} + +#ifdef DEBUGGING_SUPPORTED +// For right now, we only check to see if the DebuggableAttribute is present - later may add fields/properties to the +// attributes. +HRESULT Assembly::GetDebuggingCustomAttributes(DWORD *pdwFlags) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + WRAPPER(GC_TRIGGERS); + MODE_ANY; + FORBID_FAULT; + PRECONDITION(CheckPointer(pdwFlags)); + } + CONTRACTL_END; + + ULONG size; + BYTE *blob; + IMDInternalImport* mdImport = GetPEAssembly()->GetMDImport(); + mdAssembly asTK = TokenFromRid(mdtAssembly, 1); + + HRESULT hr = mdImport->GetCustomAttributeByName(asTK, + DEBUGGABLE_ATTRIBUTE_TYPE, + (const void**)&blob, + &size); + + // If there is no custom value, then there is no entrypoint defined. + if (FAILED(hr) || hr == S_FALSE) + return hr; + + // 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(), + DEBUGGABLE_ATTRIBUTE_TYPE_NAME, + blob[2], blob[3], *pdwFlags)); + } + + return hr; +} +#endif // DEBUGGING_SUPPORTED + +BOOL Assembly::NotifyDebuggerLoad(int flags, BOOL attaching) +{ + WRAPPER_NO_CONTRACT; + + BOOL result = 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(GetDomainAssembly()); + } + result = TRUE; + } + + if(this->ShouldNotifyDebugger()) + { + result = result || + this->GetModule()->NotifyDebuggerLoad(GetDomainAssembly(), flags, attaching); + } + + if( ShouldNotifyDebugger()) + { + result |= m_pModule->NotifyDebuggerLoad(GetDomainAssembly(), ATTACH_MODULE_LOAD, attaching); + SetDebuggerNotified(); + } + + return result; +} + +void Assembly::NotifyDebuggerUnload() +{ + LIMITED_METHOD_CONTRACT; + + if (!AppDomain::GetCurrentDomain()->IsDebuggerAttached()) + return; + + // Dispatch module unload for the module. Debugger is resilient in case we haven't dispatched + // a previous load event (such as if debugger attached after the modules was loaded). + this->GetModule()->NotifyDebuggerUnload(); + + g_pDebugInterface->UnloadAssembly(GetDomainAssembly()); +} FriendAssemblyDescriptor::FriendAssemblyDescriptor() { @@ -1960,7 +2548,6 @@ FriendAssemblyDescriptor::~FriendAssemblyDescriptor() } } - //--------------------------------------------------------------------------------------- // // Builds a FriendAssemblyDescriptor for a given assembly @@ -2144,5 +2731,3 @@ bool FriendAssemblyDescriptor::IsAssemblyOnList(PEAssembly *pAssembly, const Arr } #endif // !DACCESS_COMPILE - - diff --git a/src/coreclr/vm/assembly.hpp b/src/coreclr/vm/assembly.hpp index f95d3596f2967..8e533370b043e 100644 --- a/src/coreclr/vm/assembly.hpp +++ b/src/coreclr/vm/assembly.hpp @@ -4,13 +4,11 @@ /*============================================================ ** ** Header: Assembly.hpp -** - ** ** Purpose: Implements assembly (loader domain) architecture ** -** ===========================================================*/ + #ifndef _ASSEMBLY_H #define _ASSEMBLY_H @@ -23,13 +21,6 @@ #include "cordbpriv.h" #include "assemblyspec.hpp" -class AppDomain; -class DomainAssembly; -class SystemDomain; -class ClassLoader; -class AssemblyNative; -class AssemblySpec; -class AllocMemTracker; class FriendAssemblyDescriptor; // Bits in m_dwDynamicAssemblyAccess (see System.Reflection.Emit.AssemblyBuilderAccess.cs) @@ -324,10 +315,23 @@ class Assembly m_debuggerFlags = flags; } + DomainAssembly* GetNextAssemblyInSameALC() + { + return m_NextAssemblyInSameALC; + } + + void SetNextAssemblyInSameALC(DomainAssembly* assembly) + { + _ASSERTE(m_NextAssemblyInSameALC == NULL); + m_NextAssemblyInSameALC = assembly; + } + private: DebuggerAssemblyControlFlags ComputeDebuggingConfig(void); - HRESULT GetDebuggingCustomAttributes(DWORD* pdwFlags); +#ifdef DEBUGGING_SUPPORTED + HRESULT GetDebuggingCustomAttributes(DWORD* pdwFlags); +#endif public: // On failure: // if loadFlag == Loader::Load => throw @@ -530,6 +534,8 @@ class Assembly DebuggerAssemblyControlFlags m_debuggerFlags; LOADERHANDLE m_hExposedObject; + + DomainAssembly* m_NextAssemblyInSameALC; }; #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/assemblyspec.hpp b/src/coreclr/vm/assemblyspec.hpp index 97aab7aa4f555..ac86f220fd5f4 100644 --- a/src/coreclr/vm/assemblyspec.hpp +++ b/src/coreclr/vm/assemblyspec.hpp @@ -6,23 +6,44 @@ ** Header: AssemblySpec.hpp ** ** Purpose: Implements classes used to bind to assemblies -** -** - - ** ===========================================================*/ + #ifndef _ASSEMBLYSPEC_H #define _ASSEMBLYSPEC_H + #include "hash.h" #include "assemblyspecbase.h" #include "domainassembly.h" #include "holder.h" -class AppDomain; -class Assembly; -class DomainAssembly; -enum FileLoadLevel; +enum FileLoadLevel +{ + // These states are tracked by FileLoadLock + + // Note: This enum must match the static array fileLoadLevelName[] + // which contains the printable names of the enum values + + // Note that semantics here are description is the LAST step done, not what is + // currently being done. + + FILE_LOAD_CREATE, + FILE_LOAD_BEGIN, + FILE_LOAD_BEFORE_TYPE_LOAD, + FILE_LOAD_EAGER_FIXUPS, + FILE_LOAD_DELIVER_EVENTS, + FILE_LOAD_VTABLE_FIXUPS, + FILE_LOADED, // Loaded by not yet active + FILE_ACTIVE // Fully active (constructors run & security checked) +}; + +enum NotificationStatus +{ + NOT_NOTIFIED=0, + PROFILER_NOTIFIED=1, + DEBUGGER_NEEDNOTIFICATION=2, + DEBUGGER_NOTIFIED=4 +}; class AssemblySpec : public BaseAssemblySpec { diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 1932bb14d71e0..f4d075b126e13 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -2917,7 +2917,7 @@ BOOL Module::NotifyDebuggerLoad(DomainAssembly * pDomainAssembly, int flags, BOO // Always capture metadata, even if no debugger is attached. If a debugger later attaches, it will use // this data. { - Module * pModule = pDomainAssembly->GetModule(); + Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); pModule->UpdateDynamicMetadataIfNeeded(); } @@ -4502,10 +4502,6 @@ void Module::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, EMEM_OUT(("MEM: %p Module\n", dac_cast(this))); } - if (m_pDomainAssembly.IsValid()) - { - m_pDomainAssembly->EnumMemoryRegions(flags); - } if (m_pPEAssembly.IsValid()) { m_pPEAssembly->EnumMemoryRegions(flags); diff --git a/src/coreclr/vm/domainassembly.cpp b/src/coreclr/vm/domainassembly.cpp index 0dfa2d8a90b99..653117ba3ecc2 100644 --- a/src/coreclr/vm/domainassembly.cpp +++ b/src/coreclr/vm/domainassembly.cpp @@ -4,15 +4,7 @@ // DomainAssembly.cpp // -// -------------------------------------------------------------------------------- - - #include "common.h" - -// -------------------------------------------------------------------------------- -// Headers -// -------------------------------------------------------------------------------- - #include "invokeutil.h" #include "eeconfig.h" #include "dynamicmethod.h" @@ -26,11 +18,6 @@ #ifndef DACCESS_COMPILE DomainAssembly::DomainAssembly(PEAssembly* pPEAssembly, LoaderAllocator* pLoaderAllocator, AllocMemTracker* memTracker) : m_pAssembly(NULL) - , m_pPEAssembly(pPEAssembly) - , m_pModule(NULL) - , m_fCollectible(pLoaderAllocator->IsCollectible()) - , m_NextDomainAssemblyInSameALC(NULL) - , m_pLoaderAllocator(pLoaderAllocator) { CONTRACTL { @@ -48,7 +35,6 @@ DomainAssembly::DomainAssembly(PEAssembly* pPEAssembly, LoaderAllocator* pLoader NewHolder assembly = Assembly::Create(pPEAssembly, memTracker, pLoaderAllocator); m_pAssembly = assembly.Extract(); - m_pModule = m_pAssembly->GetModule(); m_pAssembly->SetDomainAssembly(this); @@ -67,638 +53,15 @@ DomainAssembly::~DomainAssembly() } CONTRACTL_END; - m_pPEAssembly->Release(); - if (m_pAssembly != NULL) { delete m_pAssembly; } } - -// Optimization intended for EnsureLoadLevel only -#include -void Assembly::EnsureLoadLevel(FileLoadLevel targetLevel) -{ - CONTRACT_VOID - { - INSTANCE_CHECK; - THROWS; - GC_TRIGGERS; - } - CONTRACT_END; - - TRIGGERSGC (); - if (IsLoading()) - { - AppDomain::GetCurrentDomain()->LoadAssembly(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 constraints.) - - RequireLoadLevel((FileLoadLevel)(targetLevel-1)); - } - else - ThrowIfError(targetLevel); - - RETURN; -} -#include - -CHECK Assembly::CheckLoadLevel(FileLoadLevel requiredLevel, BOOL deadlockOK) -{ - CONTRACTL - { - INSTANCE_CHECK; - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; - - if (deadlockOK) - { - // 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(AppDomain::GetCurrentDomain()->CheckLoading(this, requiredLevel)); - } - else - { - CHECK_MSG(m_level >= requiredLevel, - "File not sufficiently loaded"); - } - - CHECK_OK; -} - - - -void Assembly::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 Assembly::SetError(Exception *ex) -{ - CONTRACT_VOID - { - PRECONDITION(!IsError()); - PRECONDITION(ex != NULL); - INSTANCE_CHECK; - THROWS; - GC_TRIGGERS; - POSTCONDITION(IsError()); - } - CONTRACT_END; - - m_pError = ex->DomainBoundClone(); - - if (m_pModule) - { - m_pModule->NotifyEtwLoadFinished(ex->GetHR()); - - if (!IsProfilerNotified()) - { - SetProfilerNotified(); - -#ifdef PROFILING_SUPPORTED - // Only send errors for non-shared assemblies; other assemblies might be successfully completed - // in another app domain later. - m_pModule->NotifyProfilerLoadFinished(ex->GetHR()); -#endif - } - } - - RETURN; -} - -void Assembly::ThrowIfError(FileLoadLevel targetLevel) -{ - CONTRACT_VOID - { - INSTANCE_CHECK; - MODE_ANY; - THROWS; - GC_TRIGGERS; - } - CONTRACT_END; - - if (m_level < targetLevel && m_pError != NULL) - { - PAL_CPP_THROW(Exception*, m_pError->DomainBoundClone()); - } - - RETURN; -} - -CHECK Assembly::CheckNoError(FileLoadLevel targetLevel) -{ - LIMITED_METHOD_CONTRACT; - CHECK(m_level >= targetLevel - || !IsError()); - - CHECK_OK; -} - -CHECK Assembly::CheckActivated() -{ - CONTRACTL - { - INSTANCE_CHECK; - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - CHECK_MSG(CheckNoError(FILE_ACTIVE), "Assembly load resulted in an error"); - - if (IsActive()) - CHECK_OK; - - // CoreLib 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 (GetPEAssembly()->IsSystem()) - CHECK_OK; - - CHECK_MSG(GetPEAssembly()->IsLoaded(), "PEAssembly has not been loaded"); - CHECK_MSG(IsLoaded(), "Assembly has not been fully loaded"); -#ifdef _DEBUG - CHECK_MSG(m_bDisableActivationCheck || CheckLoadLevel(FILE_ACTIVE), "File has not had execution verified"); -#endif - CHECK_OK; -} - #endif //!DACCESS_COMPILE -#ifndef DACCESS_COMPILE - -BOOL Assembly::DoIncrementalLoad(FileLoadLevel level) -{ - STANDARD_VM_CONTRACT; - - if (IsError()) - return FALSE; - - switch (level) - { - case FILE_LOAD_BEGIN: - Begin(); - break; - - case FILE_LOAD_BEFORE_TYPE_LOAD: - BeforeTypeLoad(); - break; - - case FILE_LOAD_EAGER_FIXUPS: - EagerFixups(); - break; - - case FILE_LOAD_DELIVER_EVENTS: - DeliverSyncEvents(); - break; - - case FILE_LOAD_VTABLE_FIXUPS: - VtableFixups(); - break; - - case FILE_LOADED: - FinishLoad(); - break; - - case FILE_ACTIVE: - Activate(); - break; - - default: - UNREACHABLE(); - } - -#ifdef FEATURE_MULTICOREJIT - { - Module * pModule = m_pModule; - - if (pModule != NULL) // Should not triggle assert when module is NULL - { - AppDomain::GetCurrentDomain()->GetMulticoreJitManager().RecordModuleLoad(pModule, level); - } - } -#endif - - return TRUE; -} - -void Assembly::BeforeTypeLoad() -{ - CONTRACTL - { - INSTANCE_CHECK; - // Note that GetPEAssembly()->EnsureLoaded must be called before this OUTSIDE OF THE LOCKS - PRECONDITION(GetPEAssembly()->IsLoaded()); - STANDARD_VM_CHECK; - } - CONTRACTL_END; - -#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 (!IsProfilerNotified()) - { - SetProfilerNotified(); - GetModule()->NotifyProfilerLoadFinished(S_OK); - } - -#endif -} - -void Assembly::EagerFixups() -{ - WRAPPER_NO_CONTRACT; - -#ifdef FEATURE_READYTORUN - if (GetModule()->IsReadyToRun()) - { - GetModule()->RunEagerFixups(); - - } -#endif // FEATURE_READYTORUN -} - -void Assembly::VtableFixups() -{ - WRAPPER_NO_CONTRACT; - - GetModule()->FixupVTables(); -} - -void Assembly::FinishLoad() -{ - CONTRACTL - { - INSTANCE_CHECK; - STANDARD_VM_CHECK; - } - CONTRACTL_END; - - // 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); -} - -void Assembly::Activate() -{ - CONTRACT_VOID - { - INSTANCE_CHECK; - PRECONDITION(IsLoaded()); - STANDARD_VM_CHECK; - } - CONTRACT_END; - - // 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. - GetModule()->IsRuntimeWrapExceptions(); - - // - // 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(); -#ifdef _DEBUG - m_bDisableActivationCheck=TRUE; -#endif - pMT->CheckRunClassInitThrowing(); - } -#ifdef _DEBUG - if (g_pConfig->ExpandModulesOnLoad()) - { - m_pModule->ExpandAll(); - } -#endif //_DEBUG - -#ifdef FEATURE_READYTORUN - if (m_pModule->IsReadyToRun()) - { - m_pModule->GetReadyToRunInfo()->RegisterUnrelatedR2RModule(); - } -#endif - - RETURN; -} - -void Assembly::Begin() -{ - STANDARD_VM_CONTRACT; - - { - AppDomain::LoadLockHolder lock(AppDomain::GetCurrentDomain()); - AppDomain::GetCurrentDomain()->AddAssembly(GetDomainAssembly()); - } - // Make it possible to find this DomainAssembly object from associated BINDER_SPACE::Assembly. - RegisterWithHostAssembly(); -} - -void Assembly::RegisterWithHostAssembly() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END - - if (GetPEAssembly()->HasHostAssembly()) - { - GetPEAssembly()->GetHostAssembly()->SetRuntimeAssembly(this); - } -} - -void Assembly::UnregisterFromHostAssembly() +PEAssembly* DomainAssembly::GetPEAssembly() { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END - - if (GetPEAssembly()->HasHostAssembly()) - { - GetPEAssembly()->GetHostAssembly()->SetRuntimeAssembly(nullptr); - } -} - -void Assembly::DeliverAsyncEvents() -{ - CONTRACTL - { - INSTANCE_CHECK; - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE); - AppDomain::GetCurrentDomain()->RaiseLoadingAssemblyEvent(this); -} - -void Assembly::DeliverSyncEvents() -{ - CONTRACTL - { - INSTANCE_CHECK; - STANDARD_VM_CHECK; - } - CONTRACTL_END; - - GetModule()->NotifyEtwLoadFinished(S_OK); - -#ifdef PROFILING_SUPPORTED - if (!IsProfilerNotified()) - { - SetProfilerNotified(); - GetModule()->NotifyProfilerLoadFinished(S_OK); - } -#endif - -#ifdef DEBUGGING_SUPPORTED - GCX_COOP(); - if (!IsDebuggerNotified()) - { - SetShouldNotifyDebugger(); - - // Still work to do even if no debugger is attached. - NotifyDebuggerLoad(ATTACH_ASSEMBLY_LOAD, FALSE); - - } -#endif // DEBUGGING_SUPPORTED + return PTR_PEAssembly(m_pAssembly->GetPEAssembly()); } - -DebuggerAssemblyControlFlags Assembly::ComputeDebuggingConfig() -{ - CONTRACTL - { - INSTANCE_CHECK; - THROWS; - WRAPPER(GC_TRIGGERS); - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - -#ifdef DEBUGGING_SUPPORTED - DWORD dacfFlags = DACF_ALLOW_JIT_OPTS; - IfFailThrow(GetDebuggingCustomAttributes(&dacfFlags)); - return (DebuggerAssemblyControlFlags)dacfFlags; -#else // !DEBUGGING_SUPPORTED - return 0; -#endif // DEBUGGING_SUPPORTED -} - -// For right now, we only check to see if the DebuggableAttribute is present - later may add fields/properties to the -// attributes. -HRESULT Assembly::GetDebuggingCustomAttributes(DWORD *pdwFlags) -{ - CONTRACTL - { - INSTANCE_CHECK; - NOTHROW; - WRAPPER(GC_TRIGGERS); - MODE_ANY; - FORBID_FAULT; - PRECONDITION(CheckPointer(pdwFlags)); - } - CONTRACTL_END; - - ULONG size; - BYTE *blob; - IMDInternalImport* mdImport = GetPEAssembly()->GetMDImport(); - mdAssembly asTK = TokenFromRid(mdtAssembly, 1); - - HRESULT hr = mdImport->GetCustomAttributeByName(asTK, - DEBUGGABLE_ATTRIBUTE_TYPE, - (const void**)&blob, - &size); - - // If there is no custom value, then there is no entrypoint defined. - if (FAILED(hr) || hr == S_FALSE) - return hr; - - // 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(), - DEBUGGABLE_ATTRIBUTE_TYPE_NAME, - blob[2], blob[3], *pdwFlags)); - } - - return hr; -} - -BOOL Assembly::NotifyDebuggerLoad(int flags, BOOL attaching) -{ - WRAPPER_NO_CONTRACT; - - BOOL result = 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(GetDomainAssembly()); - } - result = TRUE; - } - - if(this->ShouldNotifyDebugger()) - { - result = result || - this->GetModule()->NotifyDebuggerLoad(GetDomainAssembly(), flags, attaching); - } - - if( ShouldNotifyDebugger()) - { - result|=m_pModule->NotifyDebuggerLoad(GetDomainAssembly(), ATTACH_MODULE_LOAD, attaching); - SetDebuggerNotified(); - } - - return result; -} - -void Assembly::NotifyDebuggerUnload() -{ - LIMITED_METHOD_CONTRACT; - - if (!AppDomain::GetCurrentDomain()->IsDebuggerAttached()) - return; - - // Dispatch module unload for the module. Debugger is resilient in case we haven't dispatched - // a previous load event (such as if debugger attached after the modules was loaded). - this->GetModule()->NotifyDebuggerUnload(); - - g_pDebugInterface->UnloadAssembly(GetDomainAssembly()); -} - -#endif // #ifndef DACCESS_COMPILE - -#ifdef DACCESS_COMPILE - -void DomainAssembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) -{ - SUPPORTS_DAC; - - DAC_ENUM_DTHIS(); - - // 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_pPEAssembly.IsValid()) - { - m_pPEAssembly->EnumMemoryRegions(flags); - } - - if (flags == CLRDATA_ENUM_MEM_HEAP2) - { - GetLoaderAllocator()->EnumMemoryRegions(flags); - } - else if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) - { - if (m_pAssembly.IsValid()) - { - m_pAssembly->EnumMemoryRegions(flags); - } - } -} - -#endif // #ifdef DACCESS_COMPILE diff --git a/src/coreclr/vm/domainassembly.h b/src/coreclr/vm/domainassembly.h index d776edba62739..e913657033ba9 100644 --- a/src/coreclr/vm/domainassembly.h +++ b/src/coreclr/vm/domainassembly.h @@ -4,53 +4,9 @@ // DomainAssembly.h // -// -------------------------------------------------------------------------------- - - #ifndef _DOMAINASSEMBLY_H_ #define _DOMAINASSEMBLY_H_ -// -------------------------------------------------------------------------------- -// Required headers -// -------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------- -// Forward class declarations -// -------------------------------------------------------------------------------- -class AppDomain; -class DomainAssembly; -class Assembly; -class Module; -class DynamicMethodTable; - -enum FileLoadLevel -{ - // These states are tracked by FileLoadLock - - // Note: This enum must match the static array fileLoadLevelName[] - // which contains the printable names of the enum values - - // Note that semantics here are description is the LAST step done, not what is - // currently being done. - - FILE_LOAD_CREATE, - FILE_LOAD_BEGIN, - FILE_LOAD_BEFORE_TYPE_LOAD, - FILE_LOAD_EAGER_FIXUPS, - FILE_LOAD_DELIVER_EVENTS, - FILE_LOAD_VTABLE_FIXUPS, - FILE_LOADED, // Loaded by not yet active - FILE_ACTIVE // Fully active (constructors run & security checked) -}; - -enum NotificationStatus -{ - NOT_NOTIFIED=0, - PROFILER_NOTIFIED=1, - DEBUGGER_NEEDNOTIFICATION=2, - DEBUGGER_NOTIFIED=4 -}; - // -------------------------------------------------------------------------------- // DomainAssembly represents an assembly loaded (or being loaded) into an app domain. It // is guaranteed to be unique per file per app domain. @@ -69,11 +25,7 @@ class DomainAssembly final DomainAssembly() {LIMITED_METHOD_CONTRACT;}; #endif - PEAssembly *GetPEAssembly() - { - LIMITED_METHOD_DAC_CONTRACT; - return PTR_PEAssembly(m_pPEAssembly); - } + PEAssembly* GetPEAssembly(); Assembly* GetAssembly() { @@ -82,64 +34,7 @@ class DomainAssembly final return m_pAssembly; } - Module* GetModule() - { - LIMITED_METHOD_CONTRACT; - - return m_pModule; - } - - LPCUTF8 GetSimpleName() - { - WRAPPER_NO_CONTRACT; - return GetPEAssembly()->GetSimpleName(); - } - -#ifdef LOGGING - LPCUTF8 GetDebugName() - { - WRAPPER_NO_CONTRACT; - return GetPEAssembly()->GetDebugName(); - } -#endif - - BOOL IsCollectible() - { - LIMITED_METHOD_CONTRACT; - return m_fCollectible; - } - - // ------------------------------------------------------------ - // Other public APIs - // ------------------------------------------------------------ - -#ifndef DACCESS_COMPILE - BOOL Equals(DomainAssembly *pAssembly) { WRAPPER_NO_CONTRACT; return GetPEAssembly()->Equals(pAssembly->GetPEAssembly()); } - BOOL Equals(PEAssembly *pPEAssembly) { WRAPPER_NO_CONTRACT; return GetPEAssembly()->Equals(pPEAssembly); } -#endif // DACCESS_COMPILE - -#ifdef DACCESS_COMPILE - void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); -#endif - - DomainAssembly* GetNextDomainAssemblyInSameALC() - { - return m_NextDomainAssemblyInSameALC; - } - - void SetNextDomainAssemblyInSameALC(DomainAssembly* domainAssembly) - { - _ASSERTE(m_NextDomainAssemblyInSameALC == NULL); - m_NextDomainAssemblyInSameALC = domainAssembly; - } - - LoaderAllocator* GetLoaderAllocator() - { - LIMITED_METHOD_CONTRACT; - return m_pLoaderAllocator; - } - - private: +private: // ------------------------------------------------------------ // Loader API // ------------------------------------------------------------ @@ -149,18 +44,7 @@ class DomainAssembly final DomainAssembly(PEAssembly* pPEAssembly, LoaderAllocator* pLoaderAllocator, AllocMemTracker* memTracker); -private: - // ------------------------------------------------------------ - // Instance data - // ------------------------------------------------------------ - PTR_Assembly m_pAssembly; - PTR_PEAssembly m_pPEAssembly; - PTR_Module m_pModule; - - BOOL m_fCollectible; - DomainAssembly* m_NextDomainAssemblyInSameALC; - PTR_LoaderAllocator m_pLoaderAllocator; }; #endif // _DOMAINASSEMBLY_H_ diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index 726205a15e7bc..535a44f96dffb 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -5438,7 +5438,7 @@ VOID ETW::EnumerationLog::IterateCollectibleLoaderAllocator(AssemblyLoaderAlloca { Assembly *pAssembly = domainAssemblyIt->GetAssembly(); // TODO: handle iterator - Module* pModule = domainAssemblyIt->GetModule(); + Module* pModule = domainAssemblyIt->GetAssembly()->GetModule(); ETW::EnumerationLog::IterateModule(pModule, enumerationOptions); if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 3611ee834d8c4..e4463d734e064 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1704,13 +1704,13 @@ BOOL AssemblyLoaderAllocator::CanUnload() DomainAssemblyIterator::DomainAssemblyIterator(DomainAssembly* pFirstAssembly) { pCurrentAssembly = pFirstAssembly; - pNextAssembly = pCurrentAssembly ? pCurrentAssembly->GetNextDomainAssemblyInSameALC() : NULL; + pNextAssembly = pCurrentAssembly ? pCurrentAssembly->GetAssembly()->GetNextAssemblyInSameALC() : NULL; } void DomainAssemblyIterator::operator++() { pCurrentAssembly = pNextAssembly; - pNextAssembly = pCurrentAssembly ? pCurrentAssembly->GetNextDomainAssemblyInSameALC() : NULL; + pNextAssembly = pCurrentAssembly ? pCurrentAssembly->GetAssembly()->GetNextAssemblyInSameALC() : NULL; } #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/loaderallocator.inl b/src/coreclr/vm/loaderallocator.inl index 374e19902fd32..c70fb52396c15 100644 --- a/src/coreclr/vm/loaderallocator.inl +++ b/src/coreclr/vm/loaderallocator.inl @@ -52,7 +52,7 @@ inline void LoaderAllocatorID::AddDomainAssembly(DomainAssembly* pAssembly) // Link domain assembly together if (m_pDomainAssembly != NULL) { - pAssembly->SetNextDomainAssemblyInSameALC(m_pDomainAssembly); + pAssembly->GetAssembly()->SetNextAssemblyInSameALC(m_pDomainAssembly); } m_pDomainAssembly = pAssembly; }