// 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. // -------------------------------------------------------------------------------- // PEFile.cpp // // -------------------------------------------------------------------------------- #include "common.h" #include "pefile.h" #include "strongname.h" #include "eecontract.h" #include "apithreadstress.h" #include "eeconfig.h" #include "product_version.h" #include "eventtrace.h" #include "dbginterface.h" #include "peimagelayout.inl" #include "dlwrap.h" #include "invokeutil.h" #ifdef FEATURE_PREJIT #include "compile.h" #endif #include "strongnameinternal.h" #include "../binder/inc/applicationcontext.hpp" #include "clrprivbinderutil.h" #include "../binder/inc/coreclrbindercommon.h" #ifdef FEATURE_PREJIT #include "compile.h" #ifdef DEBUGGING_SUPPORTED SVAL_IMPL_INIT(DWORD, PEFile, s_NGENDebugFlags, 0); #endif #endif #include "sha1.h" #ifndef DACCESS_COMPILE // ================================================================================ // PEFile class - this is an abstract base class for PEModule and PEAssembly // @todo: rename TargetFile // ================================================================================ PEFile::PEFile(PEImage *identity, BOOL fCheckAuthenticodeSignature/*=TRUE*/) : #if _DEBUG m_pDebugName(NULL), #endif m_identity(NULL), m_openedILimage(NULL), #ifdef FEATURE_PREJIT m_nativeImage(NULL), m_fCanUseNativeImage(TRUE), #endif m_MDImportIsRW_Debugger_Use_Only(FALSE), m_bHasPersistentMDImport(FALSE), m_pMDImport(NULL), m_pImporter(NULL), m_pEmitter(NULL), m_pMetadataLock(::new SimpleRWLock(PREEMPTIVE, LOCK_TYPE_DEFAULT)), m_refCount(1), m_hash(NULL), m_flags(0), m_fStrongNameVerified(FALSE) ,m_pHostAssembly(nullptr) ,m_pFallbackLoadContextBinder(nullptr) { CONTRACTL { CONSTRUCTOR_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; if (identity) { identity->AddRef(); m_identity = identity; if(identity->IsOpened()) { //already opened, prepopulate identity->AddRef(); m_openedILimage = identity; } } } PEFile::~PEFile() { CONTRACTL { DESTRUCTOR_CHECK; NOTHROW; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; ReleaseMetadataInterfaces(TRUE); if (m_hash != NULL) delete m_hash; #ifdef FEATURE_PREJIT if (m_nativeImage != NULL) { MarkNativeImageInvalidIfOwned(); m_nativeImage->Release(); } #endif //FEATURE_PREJIT if (m_openedILimage != NULL) m_openedILimage->Release(); if (m_identity != NULL) m_identity->Release(); if (m_pMetadataLock) delete m_pMetadataLock; if (m_pHostAssembly != NULL) { m_pHostAssembly->Release(); } } #ifndef DACCESS_COMPILE void PEFile::ReleaseIL() { WRAPPER_NO_CONTRACT; if (m_openedILimage!=NULL ) { ReleaseMetadataInterfaces(TRUE, TRUE); if (m_identity != NULL) { m_identity->Release(); m_identity=NULL; } m_openedILimage->Release(); m_openedILimage = NULL; } } #endif /* static */ PEFile *PEFile::Open(PEImage *image) { CONTRACT(PEFile *) { PRECONDITION(image != NULL); PRECONDITION(image->CheckFormat()); POSTCONDITION(RETVAL != NULL); POSTCONDITION(!RETVAL->IsModule()); POSTCONDITION(!RETVAL->IsAssembly()); THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACT_END; PEFile *pFile = new PEFile(image, FALSE); if (image->HasNTHeaders() && image->HasCorHeader()) pFile->OpenMDImport_Unsafe(); //no one else can see the object yet #if _DEBUG pFile->m_debugName = image->GetPath(); pFile->m_debugName.Normalize(); pFile->m_pDebugName = pFile->m_debugName; #endif RETURN pFile; } // ------------------------------------------------------------ // Loader support routines // ------------------------------------------------------------ template void CoTaskFree(T *p) { if (p != NULL) { p->T::~T(); CoTaskMemFree(p); } } NEW_WRAPPER_TEMPLATE1(CoTaskNewHolder, CoTaskFree<_TYPE>); //----------------------------------------------------------------------------------------------------- // Catch attempts to load x64 assemblies on x86, etc. //----------------------------------------------------------------------------------------------------- static void ValidatePEFileMachineType(PEFile *peFile) { STANDARD_VM_CONTRACT; if (peFile->IsIntrospectionOnly()) return; // ReflectionOnly assemblies permitted to violate CPU restrictions if (peFile->IsDynamic()) return; // PEFiles for ReflectionEmit assemblies don't cache the machine type. if (peFile->IsResource()) return; // PEFiles for resource assemblies don't cache the machine type. if (peFile->HasNativeImage()) return; // If it passed the native binder, no need to do the check again esp. at the risk of inviting an IL page-in. DWORD peKind; DWORD actualMachineType; peFile->GetPEKindAndMachine(&peKind, &actualMachineType); if (actualMachineType == IMAGE_FILE_MACHINE_I386 && ((peKind & (peILonly | pe32BitRequired)) == peILonly)) return; // Image is marked CPU-agnostic. if (actualMachineType != IMAGE_FILE_MACHINE_NATIVE && actualMachineType != IMAGE_FILE_MACHINE_NATIVE_NI) { #ifdef _TARGET_AMD64_ // v4.0 64-bit compatibility workaround. The 64-bit v4.0 CLR's Reflection.Load(byte[]) api does not detect cpu-matches. We should consider fixing that in // the next SxS release. In the meantime, this bypass will retain compat for 64-bit v4.0 CLR for target platforms that existed at the time. // // Though this bypass kicks in for all Load() flavors, the other Load() flavors did detect cpu-matches through various other code paths that still exist. // Or to put it another way, this #ifdef makes the (4.5 only) ValidatePEFileMachineType() a NOP for x64, hence preserving 4.0 compatibility. if (actualMachineType == IMAGE_FILE_MACHINE_I386 || actualMachineType == IMAGE_FILE_MACHINE_IA64) return; #endif // _WIN64_ // Image has required machine that doesn't match the CLR. StackSString name; if (peFile->IsAssembly()) ((PEAssembly*)peFile)->GetDisplayName(name); else name = StackSString(SString::Utf8, peFile->GetSimpleName()); COMPlusThrow(kBadImageFormatException, IDS_CLASSLOAD_WRONGCPU, name.GetUnicode()); } return; // If we got here, all is good. } void PEFile::LoadLibrary(BOOL allowNativeSkip/*=TRUE*/) // if allowNativeSkip==FALSE force IL image load { CONTRACT_VOID { INSTANCE_CHECK; POSTCONDITION(CheckLoaded()); STANDARD_VM_CHECK; } CONTRACT_END; // Catch attempts to load x64 assemblies on x86, etc. ValidatePEFileMachineType(this); // See if we've already loaded it. if (CheckLoaded(allowNativeSkip)) { RETURN; } // Note that we may be racing other threads here, in the case of domain neutral files // Resource images are always flat. if (IsResource()) { GetILimage()->LoadNoMetaData(IsIntrospectionOnly()); RETURN; } #if !defined(_TARGET_64BIT_) if (!HasNativeImage() && (!GetILimage()->Has32BitNTHeaders()) && !IsIntrospectionOnly()) { // Tried to load 64-bit assembly on 32-bit platform. EEFileLoadException::Throw(this, COR_E_BADIMAGEFORMAT, NULL); } #endif // We need contents now if (!HasNativeImage()) { EnsureImageOpened(); } if (IsIntrospectionOnly()) { GetILimage()->LoadForIntrospection(); RETURN; } //---- Below this point, only do the things necessary for execution ---- _ASSERTE(!IsIntrospectionOnly()); #ifdef FEATURE_PREJIT // For on-disk Dlls, we can call LoadLibrary if (IsDll() && !((HasNativeImage()?m_nativeImage:GetILimage())->GetPath().IsEmpty())) { // Note that we may get a DllMain notification inside here. if (allowNativeSkip && HasNativeImage()) { m_nativeImage->Load(); if(!m_nativeImage->IsNativeILILOnly()) GetILimage()->Load(); // For IJW we have to load IL also... } else GetILimage()->Load(); } else #endif // FEATURE_PREJIT { // Since we couldn't call LoadLibrary, we must be an IL only image // or the image may still contain unfixed up stuff // Note that we make an exception for CompilationDomains, since PEImage // will map non-ILOnly images in a compilation domain. if (!GetILimage()->IsILOnly() && !GetAppDomain()->IsCompilationDomain()) { if (!GetILimage()->HasV1Metadata()) ThrowHR(COR_E_FIXUPSINEXE); // @todo: better error } // If we are already mapped, we can just use the current image. #ifdef FEATURE_PREJIT if (allowNativeSkip && HasNativeImage()) { m_nativeImage->LoadFromMapped(); if( !m_nativeImage->IsNativeILILOnly()) GetILimage()->LoadFromMapped(); // For IJW we have to load IL also... } else #endif { if (GetILimage()->IsFile()) { #ifdef PLATFORM_UNIX if (GetILimage()->IsILOnly()) { GetILimage()->Load(); } else #endif // PLATFORM_UNIX { GetILimage()->LoadFromMapped(); } } else { GetILimage()->LoadNoFile(); } } } RETURN; } void PEFile::SetLoadedHMODULE(HMODULE hMod) { CONTRACT_VOID { INSTANCE_CHECK; PRECONDITION(CheckPointer(hMod)); POSTCONDITION(CheckLoaded()); THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACT_END; // See if the image is an internal PEImage. GetILimage()->SetLoadedHMODULE(hMod); RETURN; } /* static */ void PEFile::DefineEmitScope( GUID iid, void **ppEmit) { CONTRACT_VOID { PRECONDITION(CheckPointer(ppEmit)); POSTCONDITION(CheckPointer(*ppEmit)); THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACT_END; SafeComHolder pDispenser; // Get the Dispenser interface. MetaDataGetDispenser( CLSID_CorMetaDataDispenser, IID_IMetaDataDispenserEx, (void **)&pDispenser); if (pDispenser == NULL) { ThrowOutOfMemory(); } // Set the option on the dispenser turn on duplicate check for TypeDef and moduleRef VARIANT varOption; V_VT(&varOption) = VT_UI4; V_I4(&varOption) = MDDupDefault | MDDupTypeDef | MDDupModuleRef | MDDupExportedType | MDDupAssemblyRef | MDDupPermission | MDDupFile; IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, &varOption)); // Set minimal MetaData size V_VT(&varOption) = VT_UI4; V_I4(&varOption) = MDInitialSizeMinimal; IfFailThrow(pDispenser->SetOption(MetaDataInitialSize, &varOption)); // turn on the thread safety! V_I4(&varOption) = MDThreadSafetyOn; IfFailThrow(pDispenser->SetOption(MetaDataThreadSafetyOptions, &varOption)); IfFailThrow(pDispenser->DefineScope(CLSID_CorMetaDataRuntime, 0, iid, (IUnknown **)ppEmit)); RETURN; } // PEFile::DefineEmitScope // ------------------------------------------------------------ // Identity // ------------------------------------------------------------ BOOL PEFile::Equals(PEFile *pFile) { CONTRACTL { INSTANCE_CHECK; PRECONDITION(CheckPointer(pFile)); GC_NOTRIGGER; NOTHROW; CANNOT_TAKE_LOCK; MODE_ANY; } CONTRACTL_END; // Same object is equal if (pFile == this) return TRUE; // Execution and introspection files are NOT equal if ( (!IsIntrospectionOnly()) != !(pFile->IsIntrospectionOnly()) ) { return FALSE; } // Different host assemblies cannot be equal unless they are associated with the same host binder // It's ok if only one has a host binder because multiple threads can race to load the same assembly // and that may cause temporary candidate PEAssembly objects that never get bound to a host assembly // because another thread beats it; the losing thread will pick up the PEAssembly in the cache. if (pFile->HasHostAssembly() && this->HasHostAssembly()) { UINT_PTR fileBinderId = 0; if (FAILED(pFile->GetHostAssembly()->GetBinderID(&fileBinderId))) return FALSE; UINT_PTR thisBinderId = 0; if (FAILED(this->GetHostAssembly()->GetBinderID(&thisBinderId))) return FALSE; if (fileBinderId != thisBinderId) return FALSE; } // Same identity is equal if (m_identity != NULL && pFile->m_identity != NULL && m_identity->Equals(pFile->m_identity)) return TRUE; // Same image is equal if (m_openedILimage != NULL && pFile->m_openedILimage != NULL && m_openedILimage->Equals(pFile->m_openedILimage)) return TRUE; return FALSE; } BOOL PEFile::Equals(PEImage *pImage) { CONTRACTL { INSTANCE_CHECK; PRECONDITION(CheckPointer(pImage)); GC_NOTRIGGER; NOTHROW; MODE_ANY; } CONTRACTL_END; // Same object is equal if (pImage == m_identity || pImage == m_openedILimage) return TRUE; #ifdef FEATURE_PREJIT if(pImage == m_nativeImage) return TRUE; #endif // Same identity is equal if (m_identity != NULL && m_identity->Equals(pImage)) return TRUE; // Same image is equal if (m_openedILimage != NULL && m_openedILimage->Equals(pImage)) return TRUE; return FALSE; } // ------------------------------------------------------------ // Descriptive strings // ------------------------------------------------------------ void PEFile::GetCodeBaseOrName(SString &result) { CONTRACTL { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; if (m_identity != NULL && !m_identity->GetPath().IsEmpty()) { result.Set(m_identity->GetPath()); } else if (IsAssembly()) { ((PEAssembly*)this)->GetCodeBase(result); } else result.SetUTF8(GetSimpleName()); } // ------------------------------------------------------------ // Checks // ------------------------------------------------------------ CHECK PEFile::CheckLoaded(BOOL bAllowNativeSkip/*=TRUE*/) { CONTRACT_CHECK { INSTANCE_CHECK; NOTHROW; GC_NOTRIGGER; SO_TOLERANT; MODE_ANY; } CONTRACT_CHECK_END; CHECK(IsLoaded(bAllowNativeSkip) // We are allowed to skip LoadLibrary in most cases for ngen'ed IL only images || (bAllowNativeSkip && HasNativeImage() && IsILOnly())); CHECK_OK; } // ------------------------------------------------------------ // Metadata access // ------------------------------------------------------------ PTR_CVOID PEFile::GetMetadata(COUNT_T *pSize) { CONTRACT(PTR_CVOID) { INSTANCE_CHECK; POSTCONDITION(CheckPointer(pSize, NULL_OK)); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); THROWS; GC_TRIGGERS; MODE_ANY; SUPPORTS_DAC; } CONTRACT_END; #ifdef FEATURE_PREJIT if (HasNativeImageMetadata()) { RETURN m_nativeImage->GetMetadata(pSize); } #endif if (IsDynamic() || !GetILimage()->HasNTHeaders() || !GetILimage()->HasCorHeader()) { if (pSize != NULL) *pSize = 0; RETURN NULL; } else { RETURN GetILimage()->GetMetadata(pSize); } } #endif // #ifndef DACCESS_COMPILE PTR_CVOID PEFile::GetLoadedMetadata(COUNT_T *pSize) { CONTRACT(PTR_CVOID) { INSTANCE_CHECK; POSTCONDITION(CheckPointer(pSize, NULL_OK)); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); NOTHROW; GC_NOTRIGGER; MODE_ANY; SUPPORTS_DAC; } CONTRACT_END; #ifdef FEATURE_PREJIT if (HasNativeImageMetadata()) { RETURN GetLoadedNative()->GetMetadata(pSize); } #endif if (!HasLoadedIL() || !GetLoadedIL()->HasNTHeaders() || !GetLoadedIL()->HasCorHeader()) { if (pSize != NULL) *pSize = 0; RETURN NULL; } else { RETURN GetLoadedIL()->GetMetadata(pSize); } } TADDR PEFile::GetIL(RVA il) { CONTRACT(TADDR) { INSTANCE_CHECK; PRECONDITION(il != 0); PRECONDITION(!IsDynamic()); PRECONDITION(!IsResource()); #ifndef DACCESS_COMPILE PRECONDITION(CheckLoaded()); #endif POSTCONDITION(RETVAL != NULL); THROWS; GC_NOTRIGGER; MODE_ANY; SUPPORTS_DAC; } CONTRACT_END; PEImageLayout *image = NULL; #ifdef FEATURE_PREJIT // Note it is important to get the IL from the native image if // available, since we are using the metadata from the native image // which has different IL rva's. if (HasNativeImageMetadata()) { image = GetLoadedNative(); #ifndef DACCESS_COMPILE // NGen images are trusted to be well-formed. _ASSERTE(image->CheckILMethod(il)); #endif } else #endif // FEATURE_PREJIT { image = GetLoadedIL(); #ifndef DACCESS_COMPILE // Verify that the IL blob is valid before giving it out if (!image->CheckILMethod(il)) COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL_RANGE); #endif } RETURN image->GetRvaData(il); } #ifndef DACCESS_COMPILE void PEFile::OpenImporter() { CONTRACTL { INSTANCE_CHECK; THROWS; GC_NOTRIGGER; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; // Make sure internal MD is in RW format. ConvertMDInternalToReadWrite(); IMetaDataImport2 *pIMDImport = NULL; IfFailThrow(GetMetaDataPublicInterfaceFromInternal((void*)GetPersistentMDImport(), IID_IMetaDataImport2, (void **)&pIMDImport)); // Atomically swap it into the field (release it if we lose the race) if (FastInterlockCompareExchangePointer(&m_pImporter, pIMDImport, NULL) != NULL) pIMDImport->Release(); } void PEFile::ConvertMDInternalToReadWrite() { CONTRACTL { INSTANCE_CHECK; THROWS; GC_NOTRIGGER; MODE_ANY; INJECT_FAULT(EX_THROW(EEMessageException, (E_OUTOFMEMORY));); } CONTRACTL_END; IMDInternalImport *pOld; // Old (current RO) value of internal import. IMDInternalImport *pNew = NULL; // New (RW) value of internal import. // Take a local copy of *ppImport. This may be a pointer to an RO // or to an RW MDInternalXX. pOld = m_pMDImport; IMetaDataImport *pIMDImport = m_pImporter; if (pIMDImport != NULL) { HRESULT hr = GetMetaDataInternalInterfaceFromPublic(pIMDImport, IID_IMDInternalImport, (void **)&pNew); if (FAILED(hr)) { EX_THROW(EEMessageException, (hr)); } if (pNew == pOld) { pNew->Release(); return; } } else { // If an RO, convert to an RW, return S_OK. If already RW, no conversion // needed, return S_FALSE. HRESULT hr = ConvertMDInternalImport(pOld, &pNew); if (FAILED(hr)) { EX_THROW(EEMessageException, (hr)); } // If no conversion took place, don't change pointers. if (hr == S_FALSE) return; } // Swap the pointers in a thread safe manner. If the contents of *ppImport // equals pOld then no other thread got here first, and the old contents are // replaced with pNew. The old contents are returned. _ASSERTE(m_bHasPersistentMDImport); if (FastInterlockCompareExchangePointer(&m_pMDImport, pNew, pOld) == pOld) { //if the debugger queries, it will now see that we have RW metadata m_MDImportIsRW_Debugger_Use_Only = TRUE; // Swapped -- get the metadata to hang onto the old Internal import. HRESULT hr=m_pMDImport->SetUserContextData(pOld); _ASSERTE(SUCCEEDED(hr)||!"Leaking old MDImport"); IfFailThrow(hr); } else { // Some other thread finished first. Just free the results of this conversion. pNew->Release(); } } void PEFile::ConvertMetadataToRWForEnC() { CONTRACTL { THROWS; GC_NOTRIGGER; SO_INTOLERANT; MODE_ANY; } CONTRACTL_END; // This should only ever be called on EnC capable files. // One can check this using Module::IsEditAndContinueCapable(). // This should only be called if we're debugging, stopped, and on the helper thread. _ASSERTE(CORDebuggerAttached()); _ASSERTE((g_pDebugInterface != NULL) && g_pDebugInterface->ThisIsHelperThread()); _ASSERTE((g_pDebugInterface != NULL) && g_pDebugInterface->IsStopped()); // Convert the metadata to RW for Edit and Continue, properly replacing the metadata import interface pointer and // properly preserving the old importer. This will be called before the EnC system tries to apply a delta to the module's // metadata. ConvertMDInternalToReadWrite() does that quite nicely for us. ConvertMDInternalToReadWrite(); } void PEFile::OpenMDImport_Unsafe() { CONTRACTL { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; if (m_pMDImport != NULL) return; #ifdef FEATURE_PREJIT if (m_nativeImage != NULL && m_nativeImage->GetMDImport() != NULL ) { // Use native image for metadata m_flags |= PEFILE_HAS_NATIVE_IMAGE_METADATA; m_pMDImport=m_nativeImage->GetMDImport(); } else #endif { #ifdef FEATURE_PREJIT m_flags &= ~PEFILE_HAS_NATIVE_IMAGE_METADATA; #endif if (!IsDynamic() && GetILimage()->HasNTHeaders() && GetILimage()->HasCorHeader()) { m_pMDImport=GetILimage()->GetMDImport(); } else ThrowHR(COR_E_BADIMAGEFORMAT); m_bHasPersistentMDImport=TRUE; } _ASSERTE(m_pMDImport); m_pMDImport->AddRef(); } void PEFile::OpenEmitter() { CONTRACTL { INSTANCE_CHECK; THROWS; GC_NOTRIGGER; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; // Make sure internal MD is in RW format. ConvertMDInternalToReadWrite(); IMetaDataEmit *pIMDEmit = NULL; IfFailThrow(GetMetaDataPublicInterfaceFromInternal((void*)GetPersistentMDImport(), IID_IMetaDataEmit, (void **)&pIMDEmit)); // Atomically swap it into the field (release it if we lose the race) if (FastInterlockCompareExchangePointer(&m_pEmitter, pIMDEmit, NULL) != NULL) pIMDEmit->Release(); } void PEFile::ReleaseMetadataInterfaces(BOOL bDestructor, BOOL bKeepNativeData/*=FALSE*/) { CONTRACTL { INSTANCE_CHECK; NOTHROW; GC_NOTRIGGER; MODE_ANY; PRECONDITION(bDestructor||m_pMetadataLock->IsWriterLock()); } CONTRACTL_END; _ASSERTE(bDestructor || !m_bHasPersistentMDImport); if (m_pImporter != NULL) { m_pImporter->Release(); m_pImporter = NULL; } if (m_pEmitter != NULL) { m_pEmitter->Release(); m_pEmitter = NULL; } if (m_pMDImport != NULL && (!bKeepNativeData || !HasNativeImage())) { m_pMDImport->Release(); m_pMDImport=NULL; } } // ------------------------------------------------------------ // PE file access // ------------------------------------------------------------ // Note that most of these APIs are currently passed through // to the main image. However, in the near future they will // be rerouted to the native image in the prejitted case so // we can avoid using the original IL image. #endif //!DACCESS_COMPILE #ifdef FEATURE_PREJIT #ifndef DACCESS_COMPILE // ------------------------------------------------------------ // Native image access // ------------------------------------------------------------ void PEFile::SetNativeImage(PEImage *image) { CONTRACT_VOID { INSTANCE_CHECK; PRECONDITION(!HasNativeImage()); STANDARD_VM_CHECK; } CONTRACT_END; _ASSERTE(image != NULL); PREFIX_ASSUME(image != NULL); if (image->GetLoadedLayout()->GetBase() != image->GetLoadedLayout()->GetPreferredBase()) { ExternalLog(LL_WARNING, W("Native image loaded at base address") LFMT_ADDR W("rather than preferred address:") LFMT_ADDR , DBG_ADDR(image->GetLoadedLayout()->GetBase()), DBG_ADDR(image->GetLoadedLayout()->GetPreferredBase())); } #ifdef FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS // In Apollo, first ask if we're supposed to be ignoring the prejitted code & // structures in NGENd images. If so, bail now and do not set m_nativeImage. We've // already set m_identity & m_openedILimage (possibly even pointing to the // NGEN/Triton image), and will use those PEImages to find and JIT IL (even if they // point to an NGENd/Tritonized image). if (ShouldTreatNIAsMSIL()) RETURN; #endif m_nativeImage = image; m_nativeImage->AddRef(); m_nativeImage->Load(); m_nativeImage->AllocateLazyCOWPages(); #if defined(_TARGET_AMD64_) && !defined(CROSSGEN_COMPILE) static ConfigDWORD configNGenReserveForJumpStubs; int percentReserveForJumpStubs = configNGenReserveForJumpStubs.val(CLRConfig::INTERNAL_NGenReserveForJumpStubs); if (percentReserveForJumpStubs != 0) { PEImageLayout * pLayout = image->GetLoadedLayout(); ExecutionManager::GetEEJitManager()->EnsureJumpStubReserve((BYTE *)pLayout->GetBase(), pLayout->GetVirtualSize(), percentReserveForJumpStubs * (pLayout->GetVirtualSize() / 100)); } #endif ExternalLog(LL_INFO100, W("Attempting to use native image %s."), image->GetPath().GetUnicode()); RETURN; } void PEFile::ClearNativeImage() { CONTRACT_VOID { INSTANCE_CHECK; PRECONDITION(HasNativeImage()); POSTCONDITION(!HasNativeImage()); THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACT_END; ExternalLog(LL_WARNING, "Discarding native image."); MarkNativeImageInvalidIfOwned(); { GCX_PREEMP(); SafeComHolderPreemp pOldImport=GetMDImportWithRef(); SimpleWriteLockHolder lock(m_pMetadataLock); EX_TRY { ReleaseMetadataInterfaces(FALSE); m_flags &= ~PEFILE_HAS_NATIVE_IMAGE_METADATA; if (m_nativeImage) m_nativeImage->Release(); m_nativeImage = NULL; // Make sure our normal image is open EnsureImageOpened(); // Reopen metadata from normal image OpenMDImport(); } EX_HOOK { RestoreMDImport(pOldImport); } EX_END_HOOK; } RETURN; } extern DWORD g_dwLogLevel; //=========================================================================================================== // Encapsulates CLR and Fusion logging for runtime verification of native images. //=========================================================================================================== static void RuntimeVerifyVLog(DWORD level, LoggableAssembly *pLogAsm, const WCHAR *fmt, va_list args) { STANDARD_VM_CONTRACT; BOOL fOutputToDebugger = (level == LL_ERROR && IsDebuggerPresent()); BOOL fOutputToLogging = LoggingOn(LF_ZAP, level); StackSString message; message.VPrintf(fmt, args); if (fOutputToLogging) { SString displayString = pLogAsm->DisplayString(); LOG((LF_ZAP, level, "%s: \"%S\"\n", "ZAP", displayString.GetUnicode())); LOG((LF_ZAP, level, "%S", message.GetUnicode())); LOG((LF_ZAP, level, "\n")); } if (fOutputToDebugger) { SString displayString = pLogAsm->DisplayString(); WszOutputDebugString(W("CLR:(")); WszOutputDebugString(displayString.GetUnicode()); WszOutputDebugString(W(") ")); WszOutputDebugString(message); WszOutputDebugString(W("\n")); } } //=========================================================================================================== // Encapsulates CLR and Fusion logging for runtime verification of native images. //=========================================================================================================== static void RuntimeVerifyLog(DWORD level, LoggableAssembly *pLogAsm, const WCHAR *fmt, ...) { STANDARD_VM_CONTRACT; // Avoid calling RuntimeVerifyVLog unless logging is on if ( ((level == LL_ERROR) && IsDebuggerPresent()) || LoggingOn(LF_ZAP, level) ) { va_list args; va_start(args, fmt); RuntimeVerifyVLog(level, pLogAsm, fmt, args); va_end(args); } } //============================================================================== static const LPCWSTR CorCompileRuntimeDllNames[NUM_RUNTIME_DLLS] = { MAKEDLLNAME_W(W("coreclr")), MAKEDLLNAME_W(W("clrjit")) }; LPCWSTR CorCompileGetRuntimeDllName(CorCompileRuntimeDlls id) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; SO_INTOLERANT; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; return CorCompileRuntimeDllNames[id]; } #ifndef CROSSGEN_COMPILE //============================================================================== // Will always return a valid HMODULE for CLR_INFO, but will return NULL for NGEN_COMPILER_INFO // if the DLL has not yet been loaded (it does not try to cause a load). // Gets set by IJitManager::LoadJit (yes, this breaks the abstraction boundary). HMODULE s_ngenCompilerDll = NULL; extern HMODULE CorCompileGetRuntimeDll(CorCompileRuntimeDlls id) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_ANY; SO_INTOLERANT; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; // Currently special cased for every entry. static_assert_no_msg(NUM_RUNTIME_DLLS == 2); static_assert_no_msg(CORECLR_INFO == 0); HMODULE hMod = NULL; // Try to load the correct DLL switch (id) { case CORECLR_INFO: hMod = GetCLRModule(); break; default: COMPlusThrowNonLocalized(kExecutionEngineException, W("Invalid runtime DLL ID")); break; } return hMod; } #endif // CROSSGEN_COMPILE //=========================================================================================================== // Helper for RuntimeVerifyNativeImageVersion(). Compares the loaded clr.dll and clrjit.dll's against // the ones the native image was compiled against. //=========================================================================================================== static BOOL RuntimeVerifyNativeImageTimestamps(const CORCOMPILE_VERSION_INFO *info, LoggableAssembly *pLogAsm) { STANDARD_VM_CONTRACT; return TRUE; } //=========================================================================================================== // Validates that an NI matches the running CLR, OS, CPU, etc. This is the entrypoint used by the CLR loader. // //=========================================================================================================== BOOL PEAssembly::CheckNativeImageVersion(PEImage *peimage) { STANDARD_VM_CONTRACT; // // Get the zap version header. Note that modules will not have version // headers - they add no additional versioning constraints from their // assemblies. // PEImageLayoutHolder image=peimage->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED); if (!image->HasNativeHeader()) return FALSE; if (!image->CheckNativeHeaderVersion()) { // Wrong native image version is fatal error on CoreCLR ThrowHR(COR_E_NI_AND_RUNTIME_VERSION_MISMATCH); } CORCOMPILE_VERSION_INFO *info = image->GetNativeVersionInfo(); if (info == NULL) return FALSE; LoggablePEAssembly logAsm(this); if (!RuntimeVerifyNativeImageVersion(info, &logAsm)) { // Wrong native image version is fatal error on CoreCLR ThrowHR(COR_E_NI_AND_RUNTIME_VERSION_MISMATCH); } CorCompileConfigFlags configFlags = PEFile::GetNativeImageConfigFlagsWithOverrides(); // Otherwise, match regardless of the instrumentation flags configFlags = (CorCompileConfigFlags) (configFlags & ~(CORCOMPILE_CONFIG_INSTRUMENTATION_NONE | CORCOMPILE_CONFIG_INSTRUMENTATION)); if ((info->wConfigFlags & configFlags) != configFlags) { return FALSE; } return TRUE; } //=========================================================================================================== // Validates that an NI matches the running CLR, OS, CPU, etc. // // For historial reasons, some versions of the runtime perform this check at native bind time (preferrred), // while others check at CLR load time. // // This is the common funnel for both versions and is agnostic to whether the "assembly" is represented // by a CLR object or Fusion object. //=========================================================================================================== BOOL RuntimeVerifyNativeImageVersion(const CORCOMPILE_VERSION_INFO *info, LoggableAssembly *pLogAsm) { STANDARD_VM_CONTRACT; if (!RuntimeVerifyNativeImageTimestamps(info, pLogAsm)) return FALSE; // // Check that the EE version numbers are the same. // if (info->wVersionMajor != CLR_MAJOR_VERSION || info->wVersionMinor != CLR_MINOR_VERSION || info->wVersionBuildNumber != CLR_BUILD_VERSION || info->wVersionPrivateBuildNumber != CLR_BUILD_VERSION_QFE) { RuntimeVerifyLog(LL_ERROR, pLogAsm, W("CLR version recorded in native image doesn't match the current CLR.")); return FALSE; } // // Check checked/free status // if (info->wBuild != #if _DEBUG CORCOMPILE_BUILD_CHECKED #else CORCOMPILE_BUILD_FREE #endif ) { RuntimeVerifyLog(LL_ERROR, pLogAsm, W("Checked/free mismatch with native image.")); return FALSE; } // // Check processor // if (info->wMachine != IMAGE_FILE_MACHINE_NATIVE_NI) { RuntimeVerifyLog(LL_ERROR, pLogAsm, W("Processor type recorded in native image doesn't match this machine's processor.")); return FALSE; } #ifndef CROSSGEN_COMPILE // // Check the processor specific ID // CORINFO_CPU cpuInfo; GetSpecificCpuInfo(&cpuInfo); if (!IsCompatibleCpuInfo(&cpuInfo, &info->cpuInfo)) { RuntimeVerifyLog(LL_ERROR, pLogAsm, W("Required CPU features recorded in native image don't match this machine's processor.")); return FALSE; } #endif // CROSSGEN_COMPILE // // The zap is up to date. // RuntimeVerifyLog(LL_INFO100, pLogAsm, W("Native image has correct version information.")); return TRUE; } #endif // !DACCESS_COMPILE /* static */ CorCompileConfigFlags PEFile::GetNativeImageConfigFlags(BOOL fForceDebug/*=FALSE*/, BOOL fForceProfiling/*=FALSE*/, BOOL fForceInstrument/*=FALSE*/) { LIMITED_METHOD_DAC_CONTRACT; CorCompileConfigFlags result = (CorCompileConfigFlags)0; // Debugging #ifdef DEBUGGING_SUPPORTED // if these have been set, the take precedence over anything else if (s_NGENDebugFlags) { if ((s_NGENDebugFlags & CORCOMPILE_CONFIG_DEBUG_NONE) != 0) { result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_DEBUG_NONE); } else { if ((s_NGENDebugFlags & CORCOMPILE_CONFIG_DEBUG) != 0) { result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_DEBUG); } } } else #endif // DEBUGGING_SUPPORTED { if (fForceDebug) { result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_DEBUG); } else { result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_DEBUG_DEFAULT); } } // Profiling #ifdef PROFILING_SUPPORTED if (fForceProfiling || CORProfilerUseProfileImages()) { result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_PROFILING); result = (CorCompileConfigFlags) (result & ~(CORCOMPILE_CONFIG_DEBUG_NONE| CORCOMPILE_CONFIG_DEBUG| CORCOMPILE_CONFIG_DEBUG_DEFAULT)); } else #endif //PROFILING_SUPPORTED result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_PROFILING_NONE); // Instrumentation #ifndef DACCESS_COMPILE BOOL instrumented = (!IsCompilationProcess() && g_pConfig->GetZapBBInstr()); #else BOOL instrumented = FALSE; #endif if (instrumented || fForceInstrument) { result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_INSTRUMENTATION); } else { result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_INSTRUMENTATION_NONE); } // NOTE: Right now we are not taking instrumentation into account when binding. return result; } CorCompileConfigFlags PEFile::GetNativeImageConfigFlagsWithOverrides() { LIMITED_METHOD_DAC_CONTRACT; BOOL fForceDebug, fForceProfiling, fForceInstrument; SystemDomain::GetCompilationOverrides(&fForceDebug, &fForceProfiling, &fForceInstrument); return PEFile::GetNativeImageConfigFlags(fForceDebug, fForceProfiling, fForceInstrument); } #ifndef DACCESS_COMPILE //=========================================================================================================== // Validates that a hard-dep matches the a parent NI's compile-time hard-dep. // // For historial reasons, some versions of the runtime perform this check at native bind time (preferrred), // while others check at CLR load time. // // This is the common funnel for both versions and is agnostic to whether the "assembly" is represented // by a CLR object or Fusion object. // //=========================================================================================================== BOOL RuntimeVerifyNativeImageDependency(const CORCOMPILE_NGEN_SIGNATURE &ngenSigExpected, const CORCOMPILE_VERSION_INFO *pActual, LoggableAssembly *pLogAsm) { STANDARD_VM_CONTRACT; if (ngenSigExpected != pActual->signature) { // Signature did not match SString displayString = pLogAsm->DisplayString(); RuntimeVerifyLog(LL_ERROR, pLogAsm, W("Rejecting native image because native image dependency %s ") W("had a different identity than expected"), displayString.GetUnicode()); return FALSE; } return TRUE; } // Wrapper function for use by parts of the runtime that actually have a CORCOMPILE_DEPENDENCY to work with. BOOL RuntimeVerifyNativeImageDependency(const CORCOMPILE_DEPENDENCY *pExpected, const CORCOMPILE_VERSION_INFO *pActual, LoggableAssembly *pLogAsm) { WRAPPER_NO_CONTRACT; return RuntimeVerifyNativeImageDependency(pExpected->signNativeImage, pActual, pLogAsm); } #endif // !DACCESS_COMPILE #ifdef DEBUGGING_SUPPORTED // // Called through ICorDebugAppDomain2::SetDesiredNGENCompilerFlags to specify // which kinds of ngen'd images fusion should load wrt debugging support // Overrides any previous settings // void PEFile::SetNGENDebugFlags(BOOL fAllowOpt) { CONTRACTL { GC_NOTRIGGER; NOTHROW; MODE_ANY; SUPPORTS_DAC; } CONTRACTL_END; if (fAllowOpt) s_NGENDebugFlags = CORCOMPILE_CONFIG_DEBUG_NONE; else s_NGENDebugFlags = CORCOMPILE_CONFIG_DEBUG; } // // Called through ICorDebugAppDomain2::GetDesiredNGENCompilerFlags to determine // which kinds of ngen'd images fusion should load wrt debugging support // void PEFile::GetNGENDebugFlags(BOOL *fAllowOpt) { CONTRACTL { GC_NOTRIGGER; NOTHROW; MODE_ANY; SUPPORTS_DAC; } CONTRACTL_END; CorCompileConfigFlags configFlags = PEFile::GetNativeImageConfigFlagsWithOverrides(); *fAllowOpt = ((configFlags & CORCOMPILE_CONFIG_DEBUG) == 0); } #endif // DEBUGGING_SUPPORTED #ifndef DACCESS_COMPILE #ifdef FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS //--------------------------------------------------------------------------------------- // // Used in Apollo, this method determines whether profiling or debugging has requested // the runtime to provide debuggable / profileable code. In other CLR builds, this would // normally result in requiring the appropriate NGEN scenario be loaded (/Debug or // /Profile) and to JIT if unavailable. In Apollo, however, these NGEN scenarios are // never available, and even MSIL assemblies are often not available. So this function // tells its caller to use the NGENd assembly as if it were an MSIL assembly--ignore the // prejitted code and prebaked structures, and just JIT code and load classes from // scratch. // // Return Value: // nonzero iff NGENd images should be treated as MSIL images. // // static BOOL PEFile::ShouldTreatNIAsMSIL() { LIMITED_METHOD_CONTRACT; // Never use fragile native image content during ReadyToRun compilation. It would // produces non-version resilient images because of wrong cached values for // MethodTable::IsLayoutFixedInCurrentVersionBubble, etc. if (IsReadyToRunCompilation()) return TRUE; // Ask profiling API & config vars whether NGENd images should be avoided // completely. if (!NGENImagesAllowed()) return TRUE; // Ask profiling and debugging if they're requesting us to use ngen /Debug or // /Profile images (which aren't available under Apollo) CorCompileConfigFlags configFlags = PEFile::GetNativeImageConfigFlagsWithOverrides(); if ((configFlags & (CORCOMPILE_CONFIG_DEBUG | CORCOMPILE_CONFIG_PROFILING)) != 0) return TRUE; return FALSE; } #endif // FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS #endif //!DACCESS_COMPILE #endif // FEATURE_PREJIT #ifndef DACCESS_COMPILE // ------------------------------------------------------------ // Resource access // ------------------------------------------------------------ void PEFile::GetEmbeddedResource(DWORD dwOffset, DWORD *cbResource, PBYTE *pbInMemoryResource) { CONTRACTL { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(ThrowOutOfMemory();); } CONTRACTL_END; // NOTE: it's not clear whether to load this from m_image or m_loadedImage. // m_loadedImage is probably preferable, but this may be called by security // before the image is loaded. PEImage *image; #ifdef FEATURE_PREJIT if (m_nativeImage != NULL) image = m_nativeImage; else #endif { EnsureImageOpened(); image = GetILimage(); } PEImageLayoutHolder theImage(image->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED)); if (!theImage->CheckResource(dwOffset)) ThrowHR(COR_E_BADIMAGEFORMAT); COUNT_T size; const void *resource = theImage->GetResource(dwOffset, &size); *cbResource = size; *pbInMemoryResource = (PBYTE) resource; } // ------------------------------------------------------------ // File loading // ------------------------------------------------------------ PEAssembly * PEFile::LoadAssembly( mdAssemblyRef kAssemblyRef, IMDInternalImport * pImport, // = NULL LPCUTF8 szWinRtTypeNamespace, // = NULL LPCUTF8 szWinRtTypeClassName) // = NULL { CONTRACT(PEAssembly *) { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; POSTCONDITION(CheckPointer(RETVAL)); INJECT_FAULT(COMPlusThrowOM();); } CONTRACT_END; if (pImport == NULL) pImport = GetPersistentMDImport(); if (((TypeFromToken(kAssemblyRef) != mdtAssembly) && (TypeFromToken(kAssemblyRef) != mdtAssemblyRef)) || (!pImport->IsValidToken(kAssemblyRef))) { ThrowHR(COR_E_BADIMAGEFORMAT); } AssemblySpec spec; spec.InitializeSpec(kAssemblyRef, pImport, GetAppDomain()->FindAssembly(GetAssembly()), IsIntrospectionOnly()); if (szWinRtTypeClassName != NULL) spec.SetWindowsRuntimeType(szWinRtTypeNamespace, szWinRtTypeClassName); RETURN GetAppDomain()->BindAssemblySpec(&spec, TRUE, IsIntrospectionOnly()); } // ------------------------------------------------------------ // Logging // ------------------------------------------------------------ #ifdef FEATURE_PREJIT void PEFile::ExternalLog(DWORD facility, DWORD level, const WCHAR *fmt, ...) { WRAPPER_NO_CONTRACT; va_list args; va_start(args, fmt); ExternalVLog(facility, level, fmt, args); va_end(args); } void PEFile::ExternalLog(DWORD level, const WCHAR *fmt, ...) { WRAPPER_NO_CONTRACT; va_list args; va_start(args, fmt); ExternalVLog(LF_ZAP, level, fmt, args); va_end(args); } void PEFile::ExternalLog(DWORD level, const char *msg) { WRAPPER_NO_CONTRACT; // It is OK to use %S here. We know that msg is ASCII-only. ExternalLog(level, W("%S"), msg); } void PEFile::ExternalVLog(DWORD facility, DWORD level, const WCHAR *fmt, va_list args) { CONTRACT_VOID { THROWS; GC_TRIGGERS; } CONTRACT_END; BOOL fOutputToDebugger = (level == LL_ERROR && IsDebuggerPresent()); BOOL fOutputToLogging = LoggingOn(facility, level); if (!fOutputToDebugger && !fOutputToLogging) return; StackSString message; message.VPrintf(fmt, args); if (fOutputToLogging) { if (GetMDImport() != NULL) LOG((facility, level, "%s: \"%s\"\n", (facility == LF_ZAP ? "ZAP" : "LOADER"), GetSimpleName())); else LOG((facility, level, "%s: \"%S\"\n", (facility == LF_ZAP ? "ZAP" : "LOADER"), ((const WCHAR *)GetPath()))); LOG((facility, level, "%S", message.GetUnicode())); LOG((facility, level, "\n")); } if (fOutputToDebugger) { WszOutputDebugString(W("CLR:(")); StackSString codebase; GetCodeBaseOrName(codebase); WszOutputDebugString(codebase); WszOutputDebugString(W(") ")); WszOutputDebugString(message); WszOutputDebugString(W("\n")); } RETURN; } void PEFile::FlushExternalLog() { LIMITED_METHOD_CONTRACT; } #endif BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource, PBYTE *pbInMemoryResource, DomainAssembly** pAssemblyRef, LPCSTR *szFileName, DWORD *dwLocation, StackCrawlMark *pStackMark, BOOL fSkipSecurityCheck, BOOL fSkipRaiseResolveEvent, DomainAssembly* pDomainAssembly, AppDomain* pAppDomain) { CONTRACTL { INSTANCE_CHECK; THROWS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); WRAPPER(GC_TRIGGERS); } CONTRACTL_END; mdToken mdLinkRef; DWORD dwResourceFlags; DWORD dwOffset; mdManifestResource mdResource; Assembly* pAssembly = NULL; PEFile* pPEFile = NULL; ReleaseHolder pImport (GetMDImportWithRef()); if (SUCCEEDED(pImport->FindManifestResourceByName(szName, &mdResource))) { pPEFile = this; IfFailThrow(pImport->GetManifestResourceProps( mdResource, NULL, //&szName, &mdLinkRef, &dwOffset, &dwResourceFlags)); } else { if (fSkipRaiseResolveEvent || pAppDomain == NULL) return FALSE; DomainAssembly* pParentAssembly = GetAppDomain()->FindAssembly(GetAssembly()); pAssembly = pAppDomain->RaiseResourceResolveEvent(pParentAssembly, szName); if (pAssembly == NULL) return FALSE; pDomainAssembly = pAssembly->GetDomainAssembly(pAppDomain); pPEFile = pDomainAssembly->GetFile(); if (FAILED(pAssembly->GetManifestImport()->FindManifestResourceByName( szName, &mdResource))) { return FALSE; } if (dwLocation != 0) { if (pAssemblyRef != NULL) *pAssemblyRef = pDomainAssembly; *dwLocation = *dwLocation | 2; // ResourceLocation.containedInAnotherAssembly } IfFailThrow(pPEFile->GetPersistentMDImport()->GetManifestResourceProps( mdResource, NULL, //&szName, &mdLinkRef, &dwOffset, &dwResourceFlags)); } switch(TypeFromToken(mdLinkRef)) { case mdtAssemblyRef: { if (pDomainAssembly == NULL) return FALSE; AssemblySpec spec; spec.InitializeSpec(mdLinkRef, GetPersistentMDImport(), pDomainAssembly, pDomainAssembly->GetFile()->IsIntrospectionOnly()); pDomainAssembly = spec.LoadDomainAssembly(FILE_LOADED); if (dwLocation) { if (pAssemblyRef) *pAssemblyRef = pDomainAssembly; *dwLocation = *dwLocation | 2; // ResourceLocation.containedInAnotherAssembly } return pDomainAssembly->GetResource(szName, cbResource, pbInMemoryResource, pAssemblyRef, szFileName, dwLocation, pStackMark, fSkipSecurityCheck, fSkipRaiseResolveEvent); } case mdtFile: if (mdLinkRef == mdFileNil) { // The resource is embedded in the manifest file #ifndef CROSSGEN_COMPILE if (!IsMrPublic(dwResourceFlags) && pStackMark && !fSkipSecurityCheck) { Assembly *pCallersAssembly = SystemDomain::GetCallersAssembly(pStackMark); if (pCallersAssembly && // full trust for interop (!pCallersAssembly->GetManifestFile()->Equals(this))) { RefSecContext sCtx(AccessCheckOptions::kMemberAccess); AccessCheckOptions accessCheckOptions( AccessCheckOptions::kMemberAccess, /*accessCheckType*/ NULL, /*pAccessContext*/ FALSE, /*throwIfTargetIsInaccessible*/ (MethodTable *) NULL /*pTargetMT*/ ); // SL: return TRUE only if the caller is critical // Desktop: return TRUE only if demanding MemberAccess succeeds if (!accessCheckOptions.DemandMemberAccessOrFail(&sCtx, NULL, TRUE /*visibilityCheck*/)) return FALSE; } } #endif // CROSSGEN_COMPILE if (dwLocation) { *dwLocation = *dwLocation | 5; // ResourceLocation.embedded | // ResourceLocation.containedInManifestFile return TRUE; } pPEFile->GetEmbeddedResource(dwOffset, cbResource, pbInMemoryResource); return TRUE; } return FALSE; default: ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_IN_MANIFESTRES); } } void PEFile::GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine) { WRAPPER_NO_CONTRACT; if (IsResource() || IsDynamic()) { if (pdwKind) *pdwKind = 0; if (pdwMachine) *pdwMachine = 0; return; } #ifdef FEATURE_PREJIT if (IsNativeLoaded()) { CONSISTENCY_CHECK(HasNativeImage()); m_nativeImage->GetNativeILPEKindAndMachine(pdwKind, pdwMachine); return; } #ifndef DACCESS_COMPILE if (!HasOpenedILimage()) { //don't want to touch the IL image unless we already have ReleaseHolder pNativeImage = GetNativeImageWithRef(); if (pNativeImage) { pNativeImage->GetNativeILPEKindAndMachine(pdwKind, pdwMachine); return; } } #endif // DACCESS_COMPILE #endif // FEATURE_PREJIT GetILimage()->GetPEKindAndMachine(pdwKind, pdwMachine); return; } ULONG PEFile::GetILImageTimeDateStamp() { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; #ifdef FEATURE_PREJIT if (IsNativeLoaded()) { CONSISTENCY_CHECK(HasNativeImage()); // The IL image's time stamp is copied to the native image. CORCOMPILE_VERSION_INFO* pVersionInfo = GetLoadedNative()->GetNativeVersionInfoMaybeNull(); if (pVersionInfo == NULL) { return 0; } else { return pVersionInfo->sourceAssembly.timeStamp; } } #endif // FEATURE_PREJIT return GetLoadedIL()->GetTimeDateStamp(); } // ================================================================================ // PEAssembly class - a PEFile which represents an assembly // ================================================================================ // Statics initialization. /* static */ void PEAssembly::Attach() { STANDARD_VM_CONTRACT; } PEAssembly::PEAssembly( CoreBindResult* pBindResultInfo, IMetaDataEmit* pEmit, PEFile *creator, BOOL system, BOOL introspectionOnly/*=FALSE*/, PEImage * pPEImageIL /*= NULL*/, PEImage * pPEImageNI /*= NULL*/, ICLRPrivAssembly * pHostAssembly /*= NULL*/) : PEFile(pBindResultInfo ? (pBindResultInfo->GetPEImage() ? pBindResultInfo->GetPEImage() : (pBindResultInfo->HasNativeImage() ? pBindResultInfo->GetNativeImage() : NULL) ): pPEImageIL? pPEImageIL:(pPEImageNI? pPEImageNI:NULL), FALSE), m_creator(clr::SafeAddRef(creator)), m_bIsFromGAC(FALSE), m_bIsOnTpaList(FALSE) ,m_fProfileAssembly(0) { CONTRACTL { CONSTRUCTOR_CHECK; PRECONDITION(CheckPointer(pEmit, NULL_OK)); PRECONDITION(CheckPointer(creator, NULL_OK)); PRECONDITION(pBindResultInfo == NULL || (pPEImageIL == NULL && pPEImageNI == NULL)); STANDARD_VM_CHECK; } CONTRACTL_END; if (introspectionOnly) { if (!system) // Implementation restriction: mscorlib.dll cannot be loaded as introspection. The architecture depends on there being exactly one mscorlib. { m_flags |= PEFILE_INTROSPECTIONONLY; } } m_flags |= PEFILE_ASSEMBLY; if (system) m_flags |= PEFILE_SYSTEM; // We check the precondition above that either pBindResultInfo is null or both pPEImageIL and pPEImageNI are, // so we'll only get a max of one native image passed in. if (pPEImageNI != NULL) { SetNativeImage(pPEImageNI); } #ifdef FEATURE_PREJIT if (pBindResultInfo && pBindResultInfo->HasNativeImage()) SetNativeImage(pBindResultInfo->GetNativeImage()); #endif // If we have no native image, we require a mapping for the file. if (!HasNativeImage() || !IsILOnly()) EnsureImageOpened(); // Initialize the status of the assembly being in the GAC, or being part of the TPA list, before // we start to do work (like strong name verification) that relies on those states to be valid. if(pBindResultInfo != nullptr) { m_bIsFromGAC = pBindResultInfo->IsFromGAC(); m_bIsOnTpaList = pBindResultInfo->IsOnTpaList(); } // Check security related stuff VerifyStrongName(); // Open metadata eagerly to minimize failure windows if (pEmit == NULL) OpenMDImport_Unsafe(); //constructor, cannot race with anything else { _ASSERTE(!m_bHasPersistentMDImport); IfFailThrow(GetMetaDataInternalInterfaceFromPublic(pEmit, IID_IMDInternalImport, (void **)&m_pMDImport)); m_pEmitter = pEmit; pEmit->AddRef(); m_bHasPersistentMDImport=TRUE; m_MDImportIsRW_Debugger_Use_Only = TRUE; } // m_pMDImport can be external // Make sure this is an assembly if (!m_pMDImport->IsValidToken(TokenFromRid(1, mdtAssembly))) ThrowHR(COR_E_ASSEMBLYEXPECTED); // Verify name eagerly LPCUTF8 szName = GetSimpleName(); if (!*szName) { ThrowHR(COR_E_BADIMAGEFORMAT, BFA_EMPTY_ASSEMDEF_NAME); } // Set the host assembly and binding context as the AssemblySpec initialization // for CoreCLR will expect to have it set. if (pHostAssembly != nullptr) { m_pHostAssembly = clr::SafeAddRef(pHostAssembly); } if(pBindResultInfo != nullptr) { // Cannot have both pHostAssembly and a coreclr based bind _ASSERTE(pHostAssembly == nullptr); pBindResultInfo->GetBindAssembly(&m_pHostAssembly); } #if _DEBUG GetCodeBaseOrName(m_debugName); m_debugName.Normalize(); m_pDebugName = m_debugName; AssemblySpec spec; spec.InitializeSpec(this); spec.GetFileOrDisplayName(ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE | ASM_DISPLAYF_PUBLIC_KEY_TOKEN, m_sTextualIdentity); #endif } PEAssembly *PEAssembly::Open( PEAssembly * pParent, PEImage * pPEImageIL, PEImage * pPEImageNI, ICLRPrivAssembly * pHostAssembly, BOOL fIsIntrospectionOnly) { STANDARD_VM_CONTRACT; PEAssembly * pPEAssembly = new PEAssembly( nullptr, // BindResult nullptr, // IMetaDataEmit pParent, // PEFile creator FALSE, // isSystem fIsIntrospectionOnly, pPEImageIL, pPEImageNI, pHostAssembly); return pPEAssembly; } PEAssembly::~PEAssembly() { CONTRACTL { DESTRUCTOR_CHECK; NOTHROW; GC_TRIGGERS; // Fusion uses crsts on AddRef/Release MODE_ANY; } CONTRACTL_END; GCX_PREEMP(); if (m_creator != NULL) m_creator->Release(); } #ifndef DACCESS_COMPILE void PEAssembly::ReleaseIL() { CONTRACTL { INSTANCE_CHECK; NOTHROW; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; GCX_PREEMP(); if (m_creator != NULL) { m_creator->Release(); m_creator=NULL; } PEFile::ReleaseIL(); } #endif /* static */ PEAssembly *PEAssembly::OpenSystem(IUnknown * pAppCtx) { STANDARD_VM_CONTRACT; PEAssembly *result = NULL; EX_TRY { result = DoOpenSystem(pAppCtx); } EX_HOOK { Exception *ex = GET_EXCEPTION(); // Rethrow non-transient exceptions as file load exceptions with proper // context if (!ex->IsTransient()) EEFileLoadException::Throw(SystemDomain::System()->BaseLibrary(), ex->GetHR(), ex); } EX_END_HOOK; return result; } /* static */ PEAssembly *PEAssembly::DoOpenSystem(IUnknown * pAppCtx) { CONTRACT(PEAssembly *) { POSTCONDITION(CheckPointer(RETVAL)); STANDARD_VM_CHECK; } CONTRACT_END; ETWOnStartup (FusionBinding_V1, FusionBindingEnd_V1); CoreBindResult bindResult; ReleaseHolder pPrivAsm; IfFailThrow(CCoreCLRBinderHelper::BindToSystem(&pPrivAsm, !IsCompilationProcess() || g_fAllowNativeImages)); if(pPrivAsm != NULL) { bindResult.Init(pPrivAsm, TRUE, TRUE); } RETURN new PEAssembly(&bindResult, NULL, NULL, TRUE, FALSE); } #ifndef CROSSGEN_COMPILE /* static */ PEAssembly *PEAssembly::OpenMemory(PEAssembly *pParentAssembly, const void *flat, COUNT_T size, BOOL isIntrospectionOnly/*=FALSE*/, CLRPrivBinderLoadFile* pBinderToUse) { STANDARD_VM_CONTRACT; PEAssembly *result = NULL; EX_TRY { result = DoOpenMemory(pParentAssembly, flat, size, isIntrospectionOnly, pBinderToUse); } EX_HOOK { Exception *ex = GET_EXCEPTION(); // Rethrow non-transient exceptions as file load exceptions with proper // context if (!ex->IsTransient()) EEFileLoadException::Throw(pParentAssembly, flat, size, ex->GetHR(), ex); } EX_END_HOOK; return result; } // Thread stress class DoOpenFlatStress : APIThreadStress { public: PEAssembly *pParentAssembly; const void *flat; COUNT_T size; DoOpenFlatStress(PEAssembly *pParentAssembly, const void *flat, COUNT_T size) : pParentAssembly(pParentAssembly), flat(flat), size(size) {LIMITED_METHOD_CONTRACT;} void Invoke() { WRAPPER_NO_CONTRACT; PEAssemblyHolder result(PEAssembly::OpenMemory(pParentAssembly, flat, size, FALSE)); } }; /* static */ PEAssembly *PEAssembly::DoOpenMemory( PEAssembly *pParentAssembly, const void *flat, COUNT_T size, BOOL isIntrospectionOnly, CLRPrivBinderLoadFile* pBinderToUse) { CONTRACT(PEAssembly *) { PRECONDITION(CheckPointer(flat)); PRECONDITION(CheckOverflow(flat, size)); PRECONDITION(CheckPointer(pParentAssembly)); STANDARD_VM_CHECK; POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; // Thread stress DoOpenFlatStress ts(pParentAssembly, flat, size); // Note that we must have a flat image stashed away for two reasons. // First, we need a private copy of the data which we can verify // before doing the mapping. And secondly, we can only compute // the strong name hash on a flat image. PEImageHolder image(PEImage::LoadFlat(flat, size)); // Need to verify that this is a CLR assembly if (!image->CheckILFormat()) ThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL); CoreBindResult bindResult; ReleaseHolder assembly; IfFailThrow(CCoreCLRBinderHelper::GetAssemblyFromImage(image, NULL, &assembly)); bindResult.Init(assembly,FALSE,FALSE); RETURN new PEAssembly(&bindResult, NULL, pParentAssembly, FALSE, isIntrospectionOnly); } #endif // !CROSSGEN_COMPILE PEAssembly* PEAssembly::Open(CoreBindResult* pBindResult, BOOL isSystem, BOOL isIntrospectionOnly) { return new PEAssembly(pBindResult,NULL,NULL,isSystem,isIntrospectionOnly); }; /* static */ PEAssembly *PEAssembly::Create(PEAssembly *pParentAssembly, IMetaDataAssemblyEmit *pAssemblyEmit, BOOL bIsIntrospectionOnly) { CONTRACT(PEAssembly *) { PRECONDITION(CheckPointer(pParentAssembly)); PRECONDITION(CheckPointer(pAssemblyEmit)); STANDARD_VM_CHECK; POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; // Set up the metadata pointers in the PEAssembly. (This is the only identity // we have.) SafeComHolder pEmit; pAssemblyEmit->QueryInterface(IID_IMetaDataEmit, (void **)&pEmit); PEAssemblyHolder pFile(new PEAssembly(NULL, pEmit, pParentAssembly, FALSE, bIsIntrospectionOnly)); RETURN pFile.Extract(); } #ifdef FEATURE_PREJIT void PEAssembly::SetNativeImage(PEImage * image) { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; } CONTRACTL_END; image->Load(); if (CheckNativeImageVersion(image)) { PEFile::SetNativeImage(image); #if 0 //Enable this code if you want to make sure we never touch the flat layout in the presence of the //ngen image. //#if defined(_DEBUG) //find all the layouts in the il image and make sure we never touch them. unsigned ignored = 0; PTR_PEImageLayout layout = m_ILimage->GetLayout(PEImageLayout::LAYOUT_FLAT, 0); if (layout != NULL) { //cache a bunch of PE metadata in the PEDecoder m_ILimage->CheckILFormat(); //we also need some of metadata (for the public key), so cache this too DWORD verifyOutputFlags; m_ILimage->VerifyStrongName(&verifyOutputFlags); //fudge this by a few pages to make sure we can still mess with the PE headers const size_t fudgeSize = 4096 * 4; ClrVirtualProtect((void*)(((char *)layout->GetBase()) + fudgeSize), layout->GetSize() - fudgeSize, 0, &ignored); layout->Release(); } #endif } else { ExternalLog(LL_WARNING, "Native image is not correct version."); } } #endif // FEATURE_PREJIT BOOL PEAssembly::IsSourceGAC() { WRAPPER_NO_CONTRACT; return m_bIsFromGAC; }; #endif // #ifndef DACCESS_COMPILE #ifndef DACCESS_COMPILE // ------------------------------------------------------------ // Hash support // ------------------------------------------------------------ void PEAssembly::VerifyStrongName() { CONTRACTL { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; // If we've already done the signature checks, we don't need to do them again. if (m_fStrongNameVerified) { return; } // Without FUSION/GAC, we need to verify SN on all assemblies, except dynamic assemblies. if (IsDynamic()) { m_flags |= PEFILE_SKIP_MODULE_HASH_CHECKS; m_fStrongNameVerified = TRUE; return; } // Next, verify the strong name, if necessary // Check format of image. Note we must delay this until after the GAC status has been // checked, to handle the case where we are not loading m_image. EnsureImageOpened(); if (m_nativeImage == NULL && !GetILimage()->IsTrustedNativeImage()) { if (!GetILimage()->CheckILFormat()) ThrowHR(COR_E_BADIMAGEFORMAT); } // Check the strong name if present. if (IsIntrospectionOnly()) { // For introspection assemblies, we don't need to check strong names and we don't // need to do module hash checks. m_flags |= PEFILE_SKIP_MODULE_HASH_CHECKS; } else { // Runtime policy on CoreCLR is to skip verification of ALL assemblies m_flags |= PEFILE_SKIP_MODULE_HASH_CHECKS; m_fStrongNameVerified = TRUE; } m_fStrongNameVerified = TRUE; } BOOL PEAssembly::IsProfileAssembly() { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; // // For now, cache the result of the check below. This cache should be removed once/if the check below // becomes cheap (e.g. does not access metadata anymore). // if (VolatileLoadWithoutBarrier(&m_fProfileAssembly) != 0) { return m_fProfileAssembly > 0; } // // In order to be a platform (profile) assembly, you must be from a trusted location (TPA list) // If we are binding by TPA list and this assembly is on it, IsSourceGAC is true => Assembly is Profile // If the assembly is a WinMD, it is automatically trusted since all WinMD scenarios are full trust scenarios. // // The check for Silverlight strongname platform assemblies is legacy backdoor. It was introduced by accidental abstraction leak // from the old Silverlight binder, people took advantage of it and we cannot easily get rid of it now. See DevDiv #710462. // BOOL bProfileAssembly = IsSourceGAC() && (IsSystem() || m_bIsOnTpaList); m_fProfileAssembly = bProfileAssembly ? 1 : -1; return bProfileAssembly; } // ------------------------------------------------------------ // Descriptive strings // ------------------------------------------------------------ // Effective path is the path of nearest parent (creator) assembly which has a nonempty path. const SString &PEAssembly::GetEffectivePath() { CONTRACTL { INSTANCE_CHECK; NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; PEAssembly *pAssembly = this; while (pAssembly->m_identity == NULL || pAssembly->m_identity->GetPath().IsEmpty()) { if (pAssembly->m_creator) pAssembly = pAssembly->m_creator->GetAssembly(); else // Unmanaged exe which loads byte[]/IStream assemblies return SString::Empty(); } return pAssembly->m_identity->GetPath(); } // Codebase is the fusion codebase or path for the assembly. It is in URL format. // Note this may be obtained from the parent PEFile if we don't have a path or fusion // assembly. // // fCopiedName means to get the "shadow copied" path rather than the original path, if applicable void PEAssembly::GetCodeBase(SString &result, BOOL fCopiedName/*=FALSE*/) { CONTRACTL { INSTANCE_CHECK; THROWS; GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; // All other cases use the file path. result.Set(GetEffectivePath()); if (!result.IsEmpty()) PathToUrl(result); } /* static */ void PEAssembly::PathToUrl(SString &string) { CONTRACTL { PRECONDITION(PEImage::CheckCanonicalFullPath(string)); THROWS; GC_NOTRIGGER; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; SString::Iterator i = string.Begin(); #if !defined(PLATFORM_UNIX) if (i[0] == W('\\')) { // Network path string.Insert(i, SL("file://")); string.Skip(i, SL("file://")); } else { // Disk path string.Insert(i, SL("file:///")); string.Skip(i, SL("file:///")); } #else // Unix doesn't have a distinction between a network or a local path _ASSERTE( i[0] == W('\\') || i[0] == W('/')); SString sss(SString::Literal, W("file://")); string.Insert(i, sss); string.Skip(i, sss); #endif while (string.Find(i, W('\\'))) { string.Replace(i, W('/')); } } void PEAssembly::UrlToPath(SString &string) { CONTRACT_VOID { THROWS; GC_NOTRIGGER; } CONTRACT_END; SString::Iterator i = string.Begin(); SString sss2(SString::Literal, W("file://")); #if !defined(PLATFORM_UNIX) SString sss3(SString::Literal, W("file:///")); if (string.MatchCaseInsensitive(i, sss3)) string.Delete(i, 8); else #endif if (string.MatchCaseInsensitive(i, sss2)) string.Delete(i, 7); while (string.Find(i, W('/'))) { string.Replace(i, W('\\')); } RETURN; } BOOL PEAssembly::FindLastPathSeparator(const SString &path, SString::Iterator &i) { #ifdef PLATFORM_UNIX SString::Iterator slash = i; SString::Iterator backSlash = i; BOOL foundSlash = path.FindBack(slash, '/'); BOOL foundBackSlash = path.FindBack(backSlash, '\\'); if (!foundSlash && !foundBackSlash) return FALSE; else if (foundSlash && !foundBackSlash) i = slash; else if (!foundSlash && foundBackSlash) i = backSlash; else i = (backSlash > slash) ? backSlash : slash; return TRUE; #else return path.FindBack(i, '\\'); #endif //PLATFORM_UNIX } // ------------------------------------------------------------ // Logging // ------------------------------------------------------------ #ifdef FEATURE_PREJIT void PEAssembly::ExternalVLog(DWORD facility, DWORD level, const WCHAR *fmt, va_list args) { CONTRACT_VOID { THROWS; GC_TRIGGERS; } CONTRACT_END; PEFile::ExternalVLog(facility, level, fmt, args); RETURN; } void PEAssembly::FlushExternalLog() { CONTRACT_VOID { THROWS; GC_TRIGGERS; } CONTRACT_END; RETURN; } #endif //FEATURE_PREJIT // ------------------------------------------------------------ // Metadata access // ------------------------------------------------------------ HRESULT PEFile::GetVersion(USHORT *pMajor, USHORT *pMinor, USHORT *pBuild, USHORT *pRevision) { CONTRACTL { INSTANCE_CHECK; PRECONDITION(CheckPointer(pMajor, NULL_OK)); PRECONDITION(CheckPointer(pMinor, NULL_OK)); PRECONDITION(CheckPointer(pBuild, NULL_OK)); PRECONDITION(CheckPointer(pRevision, NULL_OK)); NOTHROW; WRAPPER(GC_TRIGGERS); MODE_ANY; } CONTRACTL_END; AssemblyMetaDataInternal md; HRESULT hr = S_OK;; if (m_bHasPersistentMDImport) { _ASSERTE(GetPersistentMDImport()->IsValidToken(TokenFromRid(1, mdtAssembly))); IfFailRet(GetPersistentMDImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, NULL, &md, NULL)); } else { ReleaseHolder pImport(GetMDImportWithRef()); _ASSERTE(pImport->IsValidToken(TokenFromRid(1, mdtAssembly))); IfFailRet(pImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, NULL, &md, NULL)); } if (pMajor != NULL) *pMajor = md.usMajorVersion; if (pMinor != NULL) *pMinor = md.usMinorVersion; if (pBuild != NULL) *pBuild = md.usBuildNumber; if (pRevision != NULL) *pRevision = md.usRevisionNumber; return hr; } void PEFile::EnsureImageOpened() { WRAPPER_NO_CONTRACT; if (IsDynamic()) return; #ifdef FEATURE_PREJIT if(HasNativeImage()) m_nativeImage->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED)->Release(); else #endif GetILimage()->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED)->Release(); } #endif // #ifndef DACCESS_COMPILE #ifdef DACCESS_COMPILE void PEFile::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; // sizeof(PEFile) == 0xb8 DAC_ENUM_VTHIS(); EMEM_OUT(("MEM: %p PEFile\n", dac_cast(this))); #ifdef _DEBUG // Not a big deal if it's NULL or fails. m_debugName.EnumMemoryRegions(flags); #endif if (m_identity.IsValid()) { m_identity->EnumMemoryRegions(flags); } if (GetILimage().IsValid()) { GetILimage()->EnumMemoryRegions(flags); } #ifdef FEATURE_PREJIT if (m_nativeImage.IsValid()) { m_nativeImage->EnumMemoryRegions(flags); DacEnumHostDPtrMem(m_nativeImage->GetLoadedLayout()->GetNativeVersionInfo()); } #endif } void PEAssembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) { WRAPPER_NO_CONTRACT; PEFile::EnumMemoryRegions(flags); if (m_creator.IsValid()) { m_creator->EnumMemoryRegions(flags); } } #endif // #ifdef DACCESS_COMPILE //------------------------------------------------------------------------------- // Make best-case effort to obtain an image name for use in an error message. // // This routine must expect to be called before the this object is fully loaded. // It can return an empty if the name isn't available or the object isn't initialized // enough to get a name, but it mustn't crash. //------------------------------------------------------------------------------- LPCWSTR PEFile::GetPathForErrorMessages() { CONTRACTL { THROWS; GC_TRIGGERS; INJECT_FAULT(COMPlusThrowOM();); SUPPORTS_DAC_HOST_ONLY; } CONTRACTL_END if (!IsDynamic()) { return m_identity->GetPathForErrorMessages(); } else { return W(""); } } #ifdef DACCESS_COMPILE TADDR PEFile::GetMDInternalRWAddress() { if (!m_MDImportIsRW_Debugger_Use_Only) return 0; else { // This line of code is a bit scary, but it is correct for now at least... // 1) We are using 'm_pMDImport_Use_Accessor' directly, and not the accessor. The field is // named this way to prevent debugger code that wants a host implementation of IMDInternalImport // from accidentally trying to use this pointer. This pointer is a target pointer, not // a host pointer. However in this function we do want the target pointer, so the usage is // accurate. // 2) ASSUMPTION: We are assuming that the only valid implementation of RW metadata is // MDInternalRW. If that ever changes we would need some way to disambiguate, and // probably this entire code path would need to be redesigned. // 3) ASSUMPTION: We are assuming that no pointer adjustment is required to convert between // IMDInternalImport*, IMDInternalImportENC* and MDInternalRW*. Ideally I was hoping to do this with a // static_cast<> but the compiler complains that the ENC<->RW is an unrelated conversion. return (TADDR) m_pMDImport_UseAccessor; } } #endif // Returns the ICLRPrivBinder* instance associated with the PEFile PTR_ICLRPrivBinder PEFile::GetBindingContext() { LIMITED_METHOD_CONTRACT; PTR_ICLRPrivBinder pBindingContext = NULL; // CoreLibrary is always bound in context of the TPA Binder. However, since it gets loaded and published // during EEStartup *before* DefaultContext Binder (aka TPAbinder) is initialized, we dont have a binding context to publish against. // Thus, we will always return NULL for its binding context. if (!IsSystem()) { pBindingContext = dac_cast(GetHostAssembly()); if (!pBindingContext) { // If we do not have any binding context, check if we are dealing with // a dynamically emitted assembly and if so, use its fallback load context // binder reference. if (IsDynamic()) { pBindingContext = GetFallbackLoadContextBinder(); } } } return pBindingContext; }