From 515a1b3d4740d6fc19a4e3ccbad3a35dfa0eaa24 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Fri, 19 Nov 2021 16:28:33 -0800 Subject: [PATCH 01/14] removed IMAGE_MAPPED --- src/coreclr/debug/daccess/request.cpp | 6 +- src/coreclr/pal/src/map/map.cpp | 4 +- src/coreclr/vm/assemblyname.cpp | 2 +- src/coreclr/vm/assemblynative.cpp | 20 +- src/coreclr/vm/methodtablebuilder.cpp | 4 +- src/coreclr/vm/nativeimage.cpp | 6 +- src/coreclr/vm/peassembly.cpp | 43 +--- src/coreclr/vm/peassembly.h | 2 +- src/coreclr/vm/peimage.cpp | 287 ++++++-------------------- src/coreclr/vm/peimage.h | 20 +- src/coreclr/vm/peimage.inl | 4 +- src/coreclr/vm/peimagelayout.cpp | 152 ++++++++------ src/coreclr/vm/peimagelayout.h | 34 +-- src/coreclr/vm/peimagelayout.inl | 6 - 14 files changed, 194 insertions(+), 396 deletions(-) diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 42b641385b5f20..fe66a8b30b4212 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -1344,11 +1344,7 @@ ClrDataAccess::GetMethodDescName(CLRDATA_ADDRESS methodDesc, unsigned int count, if (pMD->IsLCGMethod() || pMD->IsILStub()) { // In heap dumps, trying to format the signature can fail - // in certain cases because StoredSigMethodDesc::m_pSig points - // to the IMAGE_MAPPED layout (in the PEImage::m_pLayouts array). - // We save only the IMAGE_LOADED layout to the heap dump. Rather - // than bloat the dump, we just drop the signature in these - // cases. + // in certain cases. str.Clear(); TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst); diff --git a/src/coreclr/pal/src/map/map.cpp b/src/coreclr/pal/src/map/map.cpp index cf93acfac201e3..21ef333d437082 100644 --- a/src/coreclr/pal/src/map/map.cpp +++ b/src/coreclr/pal/src/map/map.cpp @@ -2043,9 +2043,9 @@ MAPmmapAndRecord( // Set the requested mapping with forced PROT_WRITE to ensure data from the file can be read there, // read the data in and finally remove the forced PROT_WRITE - if ((mprotect(pvBaseAddress, len + adjust, prot | PROT_WRITE) == -1) || + if ((mprotect(pvBaseAddress, len + adjust, PROT_WRITE) == -1) || (pread(fd, pvBaseAddress, len + adjust, offset - adjust) == -1) || - (((prot & PROT_WRITE) == 0) && mprotect(pvBaseAddress, len + adjust, prot) == -1)) + (mprotect(pvBaseAddress, len + adjust, prot) == -1)) { palError = FILEGetLastErrorFromErrno(); } diff --git a/src/coreclr/vm/assemblyname.cpp b/src/coreclr/vm/assemblyname.cpp index c04a01408bbf87..74fc7799bef008 100644 --- a/src/coreclr/vm/assemblyname.cpp +++ b/src/coreclr/vm/assemblyname.cpp @@ -56,7 +56,7 @@ FCIMPL1(Object*, AssemblyNameNative::GetFileInformation, StringObject* filenameU // waiting for it to happen during HasNTHeaders. This allows us to // get the assembly name for images that contain native code for a // non-native platform. - PEImageLayout* pLayout = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_FLAT); + PEImageLayout* pLayout = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); pImage->VerifyIsAssembly(); diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 82717ec56b0a88..a51280e21e4a34 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -138,20 +138,8 @@ Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pIma CONTRACT_END; Assembly *pLoadedAssembly = NULL; - ReleaseHolder pAssembly; - // Force the image to be loaded and mapped so that subsequent loads do not - // map a duplicate copy. - if (pImage->IsFile()) - { - pImage->Load(); - } - else - { - pImage->LoadNoFile(); - } - DWORD dwMessageID = IDS_EE_FILELOAD_ERROR_GENERIC; // Set the caller's assembly to be CoreLib @@ -251,11 +239,7 @@ extern "C" void QCALLTYPE AssemblyNative_LoadFromStream(INT_PTR ptrNativeAssembl _ASSERTE((ptrAssemblyArray != NULL) && (cbAssemblyArrayLength > 0)); _ASSERTE((ptrSymbolArray == NULL) || (cbSymbolArrayLength > 0)); - // We must have a flat image stashed away since we need a private - // copy of the data which we can verify before doing the mapping. - PVOID pAssemblyArray = reinterpret_cast(ptrAssemblyArray); - - PEImageHolder pILImage(PEImage::LoadFlat(pAssemblyArray, (COUNT_T)cbAssemblyArrayLength)); + PEImageHolder pILImage(PEImage::CreateFromByteArray((BYTE*)ptrAssemblyArray, (COUNT_T)cbAssemblyArrayLength)); // Need to verify that this is a valid CLR assembly. if (!pILImage->CheckILFormat()) @@ -318,7 +302,7 @@ extern "C" void QCALLTYPE AssemblyNative_LoadFromInMemoryModule(INT_PTR ptrNativ _ASSERTE(ptrNativeAssemblyBinder != NULL); _ASSERTE(hModule != NULL); - PEImageHolder pILImage(PEImage::LoadImage((HMODULE)hModule)); + PEImageHolder pILImage(PEImage::CreateFromHMODULE((HMODULE)hModule)); // Need to verify that this is a valid CLR assembly. if (!pILImage->HasCorHeader()) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index d5feeae4425e24..f2ca418089ee55 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -4337,8 +4337,8 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, DWORD rva; IfFailThrow(pInternalImport->GetFieldRVA(pFD->GetMemberDef(), &rva)); - // Ensure that the IL image is loaded. - GetModule()->GetPEAssembly()->EnsureLoaded(); + // The PE should be loaded by now. + _ASSERT(GetModule()->GetPEAssembly()->IsLoaded()); DWORD fldSize; if (FieldDescElementType == ELEMENT_TYPE_VALUETYPE) diff --git a/src/coreclr/vm/nativeimage.cpp b/src/coreclr/vm/nativeimage.cpp index 777ab182ee8f66..3bb47cff1acb31 100644 --- a/src/coreclr/vm/nativeimage.cpp +++ b/src/coreclr/vm/nativeimage.cpp @@ -150,11 +150,11 @@ NativeImage *NativeImage::Open( // Composite r2r PE image is not a part of anyone's identity. // We only need it to obtain the native image, which will be cached at AppDomain level. PEImageHolder pImage = PEImage::OpenImage(fullPath, MDInternalImport_NoCache, bundleFileLocation); - PEImageLayout* mapped = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_MAPPED); + PEImageLayout* loaded = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_LOADED); // We will let pImage instance be freed after exiting this scope, but we will keep the layout, // thus the layout needs an AddRef, or it will be gone together with pImage. - mapped->AddRef(); - peLoadedImage = mapped; + loaded->AddRef(); + peLoadedImage = loaded; } if (peLoadedImage.IsNull()) diff --git a/src/coreclr/vm/peassembly.cpp b/src/coreclr/vm/peassembly.cpp index 956512a3dbe062..01447537693206 100644 --- a/src/coreclr/vm/peassembly.cpp +++ b/src/coreclr/vm/peassembly.cpp @@ -77,16 +77,18 @@ void PEAssembly::EnsureLoaded() } CONTRACT_END; - // Catch attempts to load x64 assemblies on x86, etc. - ValidatePEFileMachineType(this); + if (IsDynamic()) + RETURN; - // See if we do not have anything to load or have already loaded it. - if (IsLoaded()) + // Ensure that loaded layout is available. + PEImageLayout* pLayout = GetPEImage()->GetOrCreateLayout(PEImageLayout::LAYOUT_LOADED); + if (pLayout == NULL) { - RETURN; + EEFileLoadException::Throw(this, COR_E_BADIMAGEFORMAT, NULL); } - // Note that we may be racing other threads here, in the case of domain neutral files + // Catch attempts to load x64 assemblies on x86, etc. + ValidatePEFileMachineType(this); #if !defined(TARGET_64BIT) if (!GetPEImage()->Has32BitNTHeaders()) @@ -96,35 +98,6 @@ void PEAssembly::EnsureLoaded() } #endif - // Since we couldn't call LoadLibrary, we must be an IL only image - // or the image may still contain unfixed up stuff - if (!GetPEImage()->IsILOnly()) - { - if (!GetPEImage()->HasV1Metadata()) - ThrowHR(COR_E_FIXUPSINEXE); // @todo: better error - } - - if (GetPEImage()->IsFile()) - { -#ifdef TARGET_UNIX - bool loadILImage = GetPEImage()->IsILOnly(); -#else // TARGET_UNIX - bool loadILImage = GetPEImage()->IsILOnly() && GetPEImage()->IsInBundle(); -#endif // TARGET_UNIX - if (loadILImage) - { - GetPEImage()->Load(); - } - else - { - GetPEImage()->LoadFromMapped(); - } - } - else - { - GetPEImage()->LoadNoFile(); - } - RETURN; } diff --git a/src/coreclr/vm/peassembly.h b/src/coreclr/vm/peassembly.h index adc19df11bc9b3..3342e835f265a3 100644 --- a/src/coreclr/vm/peassembly.h +++ b/src/coreclr/vm/peassembly.h @@ -65,7 +65,7 @@ typedef VPTR(PEAssembly) PTR_PEAssembly; // 1. HMODULE - these PE Files are loaded in response to "spontaneous" OS callbacks. // These should only occur for .exe main modules and IJW dlls loaded via LoadLibrary // or static imports in umnanaged code. -// These get their PEImage loaded directly in PEImage::LoadImage(HMODULE hMod) +// These get their PEImage loaded directly in PEImage::CreateFromHMODULE(HMODULE hMod) // // 2. Assemblies loaded directly or indirectly by the managed code - these are the most // common case. A path is obtained from assembly binding and the result is loaded diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index 25aeb9625ec665..9ed92be8f40baf 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -726,8 +726,6 @@ void PEImage::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) // No lock here as the processs should be suspended. if (m_pLayouts[IMAGE_FLAT].IsValid() && m_pLayouts[IMAGE_FLAT]!=NULL) m_pLayouts[IMAGE_FLAT]->EnumMemoryRegions(flags); - if (m_pLayouts[IMAGE_MAPPED].IsValid() && m_pLayouts[IMAGE_MAPPED]!=NULL) - m_pLayouts[IMAGE_MAPPED]->EnumMemoryRegions(flags); if (m_pLayouts[IMAGE_LOADED].IsValid() && m_pLayouts[IMAGE_LOADED]!=NULL) m_pLayouts[IMAGE_LOADED]->EnumMemoryRegions(flags); } @@ -816,38 +814,38 @@ PTR_PEImageLayout PEImage::GetOrCreateLayoutInternal(DWORD imageLayoutMask) if (pRetVal==NULL) { - _ASSERTE(HasPath()); + // no-path layouts are filled up at creation, if image format permits. + if (!HasPath()) + { + // TODO: VS we should manually map flat layouts for R2R here. + ThrowHR(COR_E_BADIMAGEFORMAT); + } - BOOL bIsMappedLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_MAPPED) != 0); + BOOL bIsLoadedLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_LOADED) != 0); BOOL bIsFlatLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_FLAT) != 0); + BOOL bIsLoadedLayoutPreferred = !bIsFlatLayoutSuitable; + #if !defined(TARGET_UNIX) - if (!IsInBundle() && bIsMappedLayoutSuitable) + if (!IsInBundle() && bIsLoadedLayoutSuitable) { - bIsFlatLayoutSuitable = FALSE; + bIsLoadedLayoutPreferred = TRUE; } #endif // !TARGET_UNIX - _ASSERTE(bIsMappedLayoutSuitable || bIsFlatLayoutSuitable); + _ASSERTE(bIsLoadedLayoutSuitable || bIsFlatLayoutSuitable); - BOOL bIsMappedLayoutRequired = !bIsFlatLayoutSuitable; - BOOL bIsFlatLayoutRequired = !bIsMappedLayoutSuitable; - - if (bIsFlatLayoutRequired - || bIsFlatLayoutSuitable) + if (bIsLoadedLayoutPreferred) { - _ASSERTE(bIsFlatLayoutSuitable); - - BOOL bPermitWriteableSections = bIsFlatLayoutRequired; - - pRetVal = PEImage::CreateLayoutFlat(bPermitWriteableSections); + _ASSERTE(bIsLoadedLayoutSuitable); + pRetVal = PEImage::CreateLayoutMapped(!bIsFlatLayoutSuitable); } if (pRetVal == NULL) { - _ASSERTE(bIsMappedLayoutSuitable); - - pRetVal = PEImage::CreateLayoutMapped(); + _ASSERTE(bIsFlatLayoutSuitable); + pRetVal = PEImage::CreateLayoutFlat(); + _ASSERTE(pRetVal != NULL); } } @@ -856,7 +854,7 @@ PTR_PEImageLayout PEImage::GetOrCreateLayoutInternal(DWORD imageLayoutMask) return pRetVal; } -PTR_PEImageLayout PEImage::CreateLayoutMapped() +PTR_PEImageLayout PEImage::CreateLayoutMapped(bool throwOnFailure) { CONTRACTL { @@ -864,78 +862,34 @@ PTR_PEImageLayout PEImage::CreateLayoutMapped() GC_TRIGGERS; MODE_ANY; PRECONDITION(m_pLayoutLock->IsWriterLock()); + PRECONDITION(IsFile()); } CONTRACTL_END; - PTR_PEImageLayout pRetVal; - PEImageLayout * pLoadLayout = NULL; HRESULT loadFailure = S_OK; - if (IsFile()) - { - // Try to load all files via LoadLibrary first. If LoadLibrary did not work, - // retry using regular mapping. - pLoadLayout = PEImageLayout::Load(this, FALSE /* bNTSafeLoad */, &loadFailure); - } - + pLoadLayout = PEImageLayout::Load(this, &loadFailure); if (pLoadLayout != NULL) { - SetLayout(IMAGE_MAPPED,pLoadLayout); - pLoadLayout->AddRef(); SetLayout(IMAGE_LOADED,pLoadLayout); - pRetVal=pLoadLayout; - } - else if (IsFile()) - { - PEImageLayoutHolder pLayout(PEImageLayout::Map(this)); - - bool fMarkAnyCpuImageAsLoaded = false; - - // Avoid mapping another image if we can. We can only do this for IL-ONLY images - // since LoadLibrary is needed if we are to actually load code (e.g. IJW). - if (pLayout->HasCorHeader()) - { - // IJW images must be successfully loaded by the OS to handle - // native dependencies, therefore they cannot be mapped. - if (!pLayout->IsILOnly()) - { - // For compat with older CoreCLR versions we will fallback to the - // COR_E_BADIMAGEFORMAT error code if a failure wasn't indicated. - loadFailure = FAILED(loadFailure) ? loadFailure : COR_E_BADIMAGEFORMAT; - EEFileLoadException::Throw(GetPath(), loadFailure); - } - - // IL only images will always be mapped. We don't bother doing a conversion - // of PE header on 64bit, as done for .NET Framework, since there is no - // appcompat burden for CoreCLR on 64bit. - fMarkAnyCpuImageAsLoaded = true; - } - - pLayout.SuppressRelease(); - - SetLayout(IMAGE_MAPPED,pLayout); - if (fMarkAnyCpuImageAsLoaded) + if (m_pLayouts[IMAGE_FLAT] == NULL) { - pLayout->AddRef(); - SetLayout(IMAGE_LOADED, pLayout); + pLoadLayout->AddRef(); + SetLayout(IMAGE_FLAT, pLoadLayout); } - pRetVal=pLayout; } - else - { - PEImageLayout* flatPE = GetOrCreateLayout(PEImageLayout::LAYOUT_FLAT); - if (!flatPE->CheckFormat() || !flatPE->IsILOnly()) - ThrowHR(COR_E_BADIMAGEFORMAT); - pRetVal=PEImageLayout::LoadFromFlat(flatPE); - SetLayout(IMAGE_MAPPED,pRetVal); + if (pLoadLayout == NULL && throwOnFailure) + { + loadFailure = FAILED(loadFailure) ? loadFailure : COR_E_BADIMAGEFORMAT; + EEFileLoadException::Throw(GetPath(), loadFailure); } - return pRetVal; + return pLoadLayout; } -PTR_PEImageLayout PEImage::CreateLayoutFlat(BOOL bPermitWriteableSections) +PTR_PEImageLayout PEImage::CreateLayoutFlat() { CONTRACTL { @@ -945,28 +899,24 @@ PTR_PEImageLayout PEImage::CreateLayoutFlat(BOOL bPermitWriteableSections) } CONTRACTL_END; - _ASSERTE(m_pLayouts[IMAGE_FLAT] == NULL); - PTR_PEImageLayout pFlatLayout = PEImageLayout::LoadFlat(this); + SetLayout(IMAGE_FLAT, pFlatLayout); - if (!bPermitWriteableSections - && pFlatLayout->CheckNTHeaders() - && pFlatLayout->HasWriteableSections()) + if (m_pLayouts[IMAGE_LOADED] == NULL && + pFlatLayout->CheckNTHeaders() && + pFlatLayout->CheckILOnly() && + !pFlatLayout->HasWriteableSections() && + !pFlatLayout->HasReadyToRunHeader()) { - pFlatLayout->Release(); - - return NULL; + pFlatLayout->AddRef(); + SetLayout(IMAGE_LOADED, pFlatLayout); } - else - { - m_pLayouts[IMAGE_FLAT] = pFlatLayout; - return pFlatLayout; - } + return pFlatLayout; } /* static */ -PTR_PEImage PEImage::LoadFlat(const void *flat, COUNT_T size) +PTR_PEImage PEImage::CreateFromByteArray(const BYTE* flat, COUNT_T size) { CONTRACT(PTR_PEImage) { @@ -979,14 +929,23 @@ PTR_PEImage PEImage::LoadFlat(const void *flat, COUNT_T size) _ASSERTE(!pLayout->IsMapped()); SimpleWriteLockHolder lock(pImage->m_pLayoutLock); - pImage->SetLayout(IMAGE_FLAT,pLayout); + + // TODO: VS allow R2R for now, meaning it will run as IL-only + if (pLayout->CheckNTHeaders() && + pLayout->CheckILOnly() && + !pLayout->HasWriteableSections()) + { + pLayout->AddRef(); + pImage->SetLayout(IMAGE_LOADED, pLayout); + } + RETURN dac_cast(pImage.Extract()); } #ifndef TARGET_UNIX /* static */ -PTR_PEImage PEImage::LoadImage(HMODULE hMod) +PTR_PEImage PEImage::CreateFromHMODULE(HMODULE hMod) { CONTRACT(PTR_PEImage) { @@ -999,153 +958,29 @@ PTR_PEImage PEImage::LoadImage(HMODULE hMod) StackSString path; WszGetModuleFileName(hMod, path); PEImageHolder pImage(PEImage::OpenImage(path, MDInternalImport_Default)); - if (pImage->HasLoadedLayout()) - RETURN dac_cast(pImage.Extract()); - - SimpleWriteLockHolder lock(pImage->m_pLayoutLock); - - if(pImage->m_pLayouts[IMAGE_LOADED]==NULL) - pImage->SetLayout(IMAGE_LOADED,PEImageLayout::CreateFromHMODULE(hMod,pImage,WszGetModuleHandle(NULL)!=hMod)); - - if(pImage->m_pLayouts[IMAGE_MAPPED]==NULL) - { - pImage->m_pLayouts[IMAGE_LOADED]->AddRef(); - pImage->SetLayout(IMAGE_MAPPED,pImage->m_pLayouts[IMAGE_LOADED]); - } - - RETURN dac_cast(pImage.Extract()); -} -#endif // !TARGET_UNIX - -void PEImage::Load() -{ - STANDARD_VM_CONTRACT; - - // Performance optimization to avoid lock acquisition - if (HasLoadedLayout()) - { - _ASSERTE(GetLoadedLayout()->IsMapped()||GetLoadedLayout()->IsILOnly()); - return; - } - - SimpleWriteLockHolder lock(m_pLayoutLock); - - // Re-check after lock is acquired as HasLoadedLayout here and the above line - // may return a different value in multi-threading environment. - if (HasLoadedLayout()) - { - return; - } - -#ifdef TARGET_UNIX - bool canUseLoadedFlat = true; -#else - bool canUseLoadedFlat = IsInBundle(); -#endif // TARGET_UNIX - - if (canUseLoadedFlat - && m_pLayouts[IMAGE_FLAT] != NULL - && m_pLayouts[IMAGE_FLAT]->CheckILOnlyFormat() - && !m_pLayouts[IMAGE_FLAT]->HasWriteableSections()) - { - // IL-only images with writeable sections are mapped in general way, - // because the writeable sections should always be page-aligned - // to make possible setting another protection bits exactly for these sections - _ASSERTE(!m_pLayouts[IMAGE_FLAT]->HasWriteableSections()); - - // As the image is IL-only, there should no be native code to execute - _ASSERTE(!m_pLayouts[IMAGE_FLAT]->HasNativeEntryPoint()); - - m_pLayouts[IMAGE_FLAT]->AddRef(); - - SetLayout(IMAGE_LOADED, m_pLayouts[IMAGE_FLAT]); - } - else + if (pImage->HasLoadedLayout()) { - if(!IsFile()) - { - _ASSERTE(m_pLayouts[IMAGE_FLAT] != NULL); - - if (!m_pLayouts[IMAGE_FLAT]->CheckILOnly()) - ThrowHR(COR_E_BADIMAGEFORMAT); - if(m_pLayouts[IMAGE_LOADED]==NULL) - SetLayout(IMAGE_LOADED,PEImageLayout::LoadFromFlat(m_pLayouts[IMAGE_FLAT])); - } - else - { - if(m_pLayouts[IMAGE_LOADED]==NULL) - SetLayout(IMAGE_LOADED,PEImageLayout::Load(this,TRUE)); - } + _ASSERTE(pImage->m_pLayouts[IMAGE_FLAT] != NULL); + RETURN dac_cast(pImage.Extract()); } -} -void PEImage::LoadFromMapped() -{ - STANDARD_VM_CONTRACT; + PTR_PEImageLayout pLayout = PEImageLayout::CreateFromHMODULE(hMod, pImage); - if (HasLoadedLayout()) - { - _ASSERTE(GetLoadedLayout()->IsMapped()); - return; - } - - PEImageLayout* pLayout = GetOrCreateLayout(PEImageLayout::LAYOUT_MAPPED); - SimpleWriteLockHolder lock(m_pLayoutLock); - if (m_pLayouts[IMAGE_LOADED] == NULL) + SimpleWriteLockHolder lock(pImage->m_pLayoutLock); + pImage->SetLayout(IMAGE_LOADED, pLayout); + if (pImage->m_pLayouts[IMAGE_FLAT] == NULL) { pLayout->AddRef(); - SetLayout(IMAGE_LOADED, pLayout); + pImage->SetLayout(IMAGE_FLAT, pLayout); } -} - -void PEImage::LoadNoFile() -{ - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION(!IsFile()); - } - CONTRACTL_END; - if (HasLoadedLayout()) - return; - - PEImageLayout* pLayout = GetExistingLayoutInternal(PEImageLayout::LAYOUT_ANY); - if (!pLayout->CheckILOnly()) - ThrowHR(COR_E_BADIMAGEFORMAT); - SimpleWriteLockHolder lock(m_pLayoutLock); - if (m_pLayouts[IMAGE_LOADED] == NULL) - { - pLayout->AddRef(); - SetLayout(IMAGE_LOADED, pLayout); - } + RETURN dac_cast(pImage.Extract()); } +#endif // !TARGET_UNIX #endif //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 PEImage::GetPathForErrorMessages() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - INJECT_FAULT(COMPlusThrowOM();); - SUPPORTS_DAC_HOST_ONLY; - } - CONTRACTL_END - - return m_path; -} - - HANDLE PEImage::GetFileHandle() { CONTRACTL diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index 679b3e024d3375..9e8d7c9ae0414d 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -73,9 +73,9 @@ class PEImage final ULONG Release(); #ifndef DACCESS_COMPILE - static PTR_PEImage LoadFlat(const void *flat, COUNT_T size); + static PTR_PEImage CreateFromByteArray(const BYTE* flat, COUNT_T size); #ifndef TARGET_UNIX - static PTR_PEImage LoadImage(HMODULE hMod); + static PTR_PEImage CreateFromHMODULE(HMODULE hMod); #endif // !TARGET_UNIX static PTR_PEImage OpenImage( LPCWSTR pPath, @@ -84,10 +84,6 @@ class PEImage final static PTR_PEImage FindByPath(LPCWSTR pPath, BOOL isInBundle = TRUE); void AddToHashMap(); - - void Load(); - void LoadNoFile(); - void LoadFromMapped(); #endif BOOL IsOpened(); @@ -100,6 +96,7 @@ class PEImage final ULONG GetPathHash(); const SString& GetPath(); const SString& GetPathToLoad(); + LPCWSTR GetPathForErrorMessages() { return GetPath(); } BOOL IsFile(); BOOL IsInBundle() const; @@ -110,8 +107,6 @@ class PEImage final HRESULT TryOpenFile(); - LPCWSTR GetPathForErrorMessages(); - void GetMVID(GUID *pMvid); BOOL HasV1Metadata(); IMDInternalImport* GetMDImport(); @@ -161,10 +156,10 @@ class PEImage final PTR_PEImageLayout GetOrCreateLayoutInternal(DWORD imageLayoutMask); // Create the mapped layout - PTR_PEImageLayout CreateLayoutMapped(); + PTR_PEImageLayout CreateLayoutMapped(bool throwOnFailure); // Create the flat layout - PTR_PEImageLayout CreateLayoutFlat(BOOL bPermitWriteableSections); + PTR_PEImageLayout CreateLayoutFlat(); void SetLayout(DWORD dwLayout, PTR_PEImageLayout pLayout); #endif @@ -284,9 +279,8 @@ class PEImage final enum { IMAGE_FLAT=0, - IMAGE_MAPPED=1, - IMAGE_LOADED=2, - IMAGE_COUNT=3 + IMAGE_LOADED=1, + IMAGE_COUNT=2 }; SimpleRWLock *m_pLayoutLock; diff --git a/src/coreclr/vm/peimage.inl b/src/coreclr/vm/peimage.inl index 8dc72d416fac3d..fe6cdc050baf42 100644 --- a/src/coreclr/vm/peimage.inl +++ b/src/coreclr/vm/peimage.inl @@ -127,8 +127,6 @@ inline PTR_PEImageLayout PEImage::GetExistingLayoutInternal(DWORD imageLayoutMas if (imageLayoutMask&PEImageLayout::LAYOUT_LOADED) pRetVal=m_pLayouts[IMAGE_LOADED]; - if (pRetVal==NULL && (imageLayoutMask & PEImageLayout::LAYOUT_MAPPED)) - pRetVal=m_pLayouts[IMAGE_MAPPED]; if (pRetVal==NULL && (imageLayoutMask & PEImageLayout::LAYOUT_FLAT)) pRetVal=m_pLayouts[IMAGE_FLAT]; @@ -155,7 +153,7 @@ inline PTR_PEImageLayout PEImage::GetLoadedLayout() inline BOOL PEImage::IsOpened() { LIMITED_METHOD_CONTRACT; - return m_pLayouts[IMAGE_LOADED]!=NULL ||m_pLayouts[IMAGE_MAPPED]!=NULL || m_pLayouts[IMAGE_FLAT] !=NULL; + return m_pLayouts[IMAGE_LOADED]!=NULL || m_pLayouts[IMAGE_FLAT] !=NULL; } diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index e4c4c274cf3091..1350675ba65ed8 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -27,8 +27,8 @@ PEImageLayout* PEImageLayout::CreateFlat(const void *flat, COUNT_T size,PEImage* return new RawImageLayout(flat,size,pOwner); } - -PEImageLayout* PEImageLayout::CreateFromHMODULE(HMODULE hModule,PEImage* pOwner, BOOL bTakeOwnership) +#ifndef TARGET_UNIX +PEImageLayout* PEImageLayout::CreateFromHMODULE(HMODULE hModule, PEImage* pOwner) { CONTRACTL { @@ -37,27 +37,40 @@ PEImageLayout* PEImageLayout::CreateFromHMODULE(HMODULE hModule,PEImage* pOwner, MODE_ANY; } CONTRACTL_END; - return new RawImageLayout(hModule,pOwner,bTakeOwnership,TRUE); -} -PEImageLayout* PEImageLayout::LoadFromFlat(PEImageLayout* pflatimage) -{ - STANDARD_VM_CONTRACT; - return new ConvertedImageLayout(pflatimage); + PEImageLayout* pLoadLayout; + if (WszGetModuleHandle(NULL) == hModule) + { + return new LoadedImageLayout(pOwner, hModule); + } + else + { + HRESULT loadFailure = S_OK; + pLoadLayout = new LoadedImageLayout(pOwner, &loadFailure); + + if (pLoadLayout == NULL) + { + loadFailure = FAILED(loadFailure) ? loadFailure : COR_E_BADIMAGEFORMAT; + EEFileLoadException::Throw(pOwner->GetPathForErrorMessages(), loadFailure); + } + } + + return pLoadLayout; } +#endif PEImageLayout* PEImageLayout::LoadConverted(PEImage* pOwner, BOOL isInBundle) { STANDARD_VM_CONTRACT; - PEImageLayoutHolder pFlat(new FlatImageLayout(pOwner)); + ReleaseHolder pFlat(new FlatImageLayout(pOwner)); if (!pFlat->CheckFormat()) ThrowHR(COR_E_BADIMAGEFORMAT); return new ConvertedImageLayout(pFlat, isInBundle); } -PEImageLayout* PEImageLayout::Load(PEImage* pOwner, BOOL bNTSafeLoad, HRESULT* returnDontThrow) +PEImageLayout* PEImageLayout::Load(PEImage* pOwner, HRESULT* loadFailure) { STANDARD_VM_CONTRACT; @@ -69,9 +82,16 @@ PEImageLayout* PEImageLayout::Load(PEImage* pOwner, BOOL bNTSafeLoad, HRESULT* r return PEImageLayout::LoadConverted(pOwner, true); } - PEImageLayoutHolder pAlloc(new LoadedImageLayout(pOwner,bNTSafeLoad,returnDontThrow)); + // TODO: VS this is always false, just to keep mapped layout alive for now (DAC failures) + if (!pOwner->IsFile()) + { + return PEImageLayout::Map(pOwner); + } + + PEImageLayoutHolder pAlloc(new LoadedImageLayout(pOwner, loadFailure)); if (pAlloc->GetBase()==NULL) return NULL; + return pAlloc.Extract(); #endif } @@ -101,7 +121,7 @@ PEImageLayout* PEImageLayout::Map(PEImage* pOwner) } CONTRACT_END; - PEImageLayoutHolder pAlloc = pOwner->GetUncompressedSize() != 0 ? + PEImageLayoutHolder pAlloc = pOwner->GetUncompressedSize() > 0 ? LoadConverted(pOwner, /* isInBundle */ true): new MappedImageLayout(pOwner); @@ -238,9 +258,13 @@ void PEImageLayout::ApplyBaseRelocations() #if defined(TARGET_UNIX) if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_EXECUTE)) != 0)) { +#ifdef __APPLE__ + dwNewProtection = PAGE_READWRITE; +#else // On SELinux, we cannot change protection that doesn't have execute access rights // to one that has it, so we need to set the protection to RWX instead of RW dwNewProtection = PAGE_EXECUTE_READWRITE; +#endif } #endif // TARGET_UNIX if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion, @@ -337,7 +361,6 @@ RawImageLayout::RawImageLayout(const void *flat, COUNT_T size, PEImage* pOwner) } CONTRACTL_END; m_pOwner=pOwner; - m_Layout=LAYOUT_FLAT; if (size) { @@ -368,36 +391,8 @@ RawImageLayout::RawImageLayout(const void *flat, COUNT_T size, PEImage* pOwner) } Init((void*)flat,size); } -RawImageLayout::RawImageLayout(const void *mapped, PEImage* pOwner, BOOL bTakeOwnership, BOOL bFixedUp) -{ - CONTRACTL - { - CONSTRUCTOR_CHECK; - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - m_pOwner=pOwner; - m_Layout=LAYOUT_MAPPED; - - if (bTakeOwnership) - { -#ifndef TARGET_UNIX - PathString wszDllName; - WszGetModuleFileName((HMODULE)mapped, wszDllName); - m_LibraryHolder=CLRLoadLibraryEx(wszDllName,NULL,GetLoadWithAlteredSearchPathFlag()); -#else // !TARGET_UNIX - _ASSERTE(!"bTakeOwnership Should not be used on TARGET_UNIX"); -#endif // !TARGET_UNIX - } - - IfFailThrow(Init((void*)mapped,(bool)(bFixedUp!=FALSE))); -} - -ConvertedImageLayout::ConvertedImageLayout(PEImageLayout* source, BOOL isInBundle) +ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source, BOOL isInBundle) { CONTRACTL { @@ -405,19 +400,19 @@ ConvertedImageLayout::ConvertedImageLayout(PEImageLayout* source, BOOL isInBundl STANDARD_VM_CHECK; } CONTRACTL_END; - m_Layout=LAYOUT_LOADED; m_pOwner=source->m_pOwner; _ASSERTE(!source->IsMapped()); m_pExceptionDir = NULL; if (!source->HasNTHeaders()) - EEFileLoadException::Throw(GetPath(), COR_E_BADIMAGEFORMAT); + EEFileLoadException::Throw(source->m_pOwner->GetPathForErrorMessages(), COR_E_BADIMAGEFORMAT); + LOG((LF_LOADER, LL_INFO100, "PEImage: Opening manually mapped stream\n")); // in bundle we may want to enable execution if the image contains R2R sections // so must ensure the mapping is compatible with that - bool enableExecution = isInBundle && + bool enableExecution = source->HasCorHeader() && source->HasReadyToRunHeader() && g_fAllowNativeImages; @@ -496,6 +491,24 @@ ConvertedImageLayout::~ConvertedImageLayout() #endif } +MappedImageLayout::~MappedImageLayout() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + +#if !defined(TARGET_UNIX) && !defined(TARGET_X86) + if (m_pExceptionDir) + { + RtlDeleteFunctionTable(m_pExceptionDir); + } +#endif +} + MappedImageLayout::MappedImageLayout(PEImage* pOwner) { CONTRACTL @@ -504,15 +517,15 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) STANDARD_VM_CHECK; } CONTRACTL_END; - m_Layout=LAYOUT_MAPPED; m_pOwner=pOwner; + m_pExceptionDir = NULL; HANDLE hFile = pOwner->GetFileHandle(); INT64 offset = pOwner->GetOffset(); _ASSERTE(pOwner->GetUncompressedSize() == 0); // If mapping was requested, try to do SEC_IMAGE mapping - LOG((LF_LOADER, LL_INFO100, "PEImage: Opening OS mapped %S (hFile %p)\n", (LPCWSTR) GetPath(), hFile)); + LOG((LF_LOADER, LL_INFO100, "PEImage: Opening OS mapped %S (hFile %p)\n", (LPCWSTR) pOwner->GetPath(), hFile)); #ifndef TARGET_UNIX _ASSERTE(!pOwner->IsInBundle()); @@ -540,7 +553,6 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) ThrowWin32(dwLastError); } - return; } @@ -589,13 +601,15 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) return; LOG((LF_LOADER, LL_INFO1000, "PEImage: image %S (hFile %p) mapped @ %p\n", - (LPCWSTR) GetPath(), hFile, (void*)m_LoadedFile)); + (LPCWSTR) pOwner->GetPath(), hFile, (void*)m_LoadedFile)); IfFailThrow(Init((void *) m_LoadedFile)); if (!HasCorHeader()) ThrowHR(COR_E_BADIMAGEFORMAT); +#endif // !TARGET_UNIX + if (HasReadyToRunHeader() && g_fAllowNativeImages) { //Do base relocation for PE, if necessary. @@ -606,12 +620,24 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) SetRelocated(); } + // Check if there is a static function table and install it. (Windows only, except x86) +#if !defined(TARGET_UNIX) && !defined(TARGET_X86) + COUNT_T cbSize = 0; + PT_RUNTIME_FUNCTION pExceptionDir = (PT_RUNTIME_FUNCTION)GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_EXCEPTION, &cbSize); + DWORD tableSize = cbSize / sizeof(T_RUNTIME_FUNCTION); + + if (pExceptionDir != NULL) + { + if (!RtlAddFunctionTable(pExceptionDir, tableSize, (DWORD64)this->GetBase())) + ThrowLastError(); -#endif // !TARGET_UNIX + m_pExceptionDir = pExceptionDir; + } +#endif //TARGET_X86 } #if !defined(TARGET_UNIX) -LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, BOOL bNTSafeLoad, HRESULT* returnDontThrow) +LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, HRESULT* loadFailure) { CONTRACTL { @@ -621,29 +647,26 @@ LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, BOOL bNTSafeLoad, HRESULT* } CONTRACTL_END; - m_Layout=LAYOUT_LOADED; m_pOwner=pOwner; DWORD dwFlags = GetLoadWithAlteredSearchPathFlag(); - if (bNTSafeLoad) - dwFlags|=DONT_RESOLVE_DLL_REFERENCES; - m_Module = CLRLoadLibraryEx(pOwner->GetPath(), NULL, dwFlags); if (m_Module == NULL) { // Fetch the HRESULT upfront before anybody gets a chance to corrupt it HRESULT hr = HRESULT_FROM_GetLastError(); - if (returnDontThrow != NULL) - { - *returnDontThrow = hr; - return; - } - - EEFileLoadException::Throw(pOwner->GetPath(), hr); + *loadFailure = HRESULT_FROM_GetLastError(); + return; } IfFailThrow(Init(m_Module,true)); - LOG((LF_LOADER, LL_INFO1000, "PEImage: Opened HMODULE %S\n", (LPCWSTR) GetPath())); + LOG((LF_LOADER, LL_INFO1000, "PEImage: Opened HMODULE %S\n", (LPCWSTR) pOwner->GetPath())); +} + +LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, HMODULE hModule) +{ + m_pOwner = pOwner; + PEDecoder::Init((void*)hModule, /* relocated */ true); } #endif // !TARGET_UNIX @@ -656,14 +679,13 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner) PRECONDITION(CheckPointer(pOwner)); } CONTRACTL_END; - m_Layout=LAYOUT_FLAT; m_pOwner=pOwner; HANDLE hFile = pOwner->GetFileHandle(); INT64 offset = pOwner->GetOffset(); INT64 size = pOwner->GetSize(); - LOG((LF_LOADER, LL_INFO100, "PEImage: Opening flat %S\n", (LPCWSTR) GetPath())); + LOG((LF_LOADER, LL_INFO100, "PEImage: Opening flat %S\n", (LPCWSTR) pOwner->GetPath())); // If a size is not specified, load the whole file if (size == 0) diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index 69e46160466afe..4804e6a127622f 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -37,20 +37,18 @@ class PEImageLayout : public PEDecoder // ------------------------------------------------------------ enum { - LAYOUT_MAPPED =1, - LAYOUT_FLAT =2, - LAYOUT_LOADED =4, - LAYOUT_LOADED_FOR_INTROSPECTION =8, - LAYOUT_ANY =0xf + LAYOUT_FLAT = 2, + LAYOUT_LOADED = 4, + LAYOUT_ANY = 0xf }; - public: #ifndef DACCESS_COMPILE static PEImageLayout* CreateFlat(const void *flat, COUNT_T size,PEImage* pOwner); - static PEImageLayout* CreateFromHMODULE(HMODULE mappedbase,PEImage* pOwner, BOOL bTakeOwnership); - static PEImageLayout* LoadFromFlat(PEImageLayout* pflatimage); - static PEImageLayout* Load(PEImage* pOwner, BOOL bNTSafeLoad, HRESULT* returnDontThrow = NULL); +#ifndef TARGET_UNIX + static PEImageLayout* CreateFromHMODULE(HMODULE hModule,PEImage* pOwner); +#endif + static PEImageLayout* Load(PEImage* pOwner, HRESULT* loadFailure); static PEImageLayout* LoadFlat(PEImage* pOwner); static PEImageLayout* LoadConverted(PEImage* pOwner, BOOL isInBundle = FALSE); static PEImageLayout* LoadNative(LPCWSTR fullPath); @@ -63,7 +61,6 @@ class PEImageLayout : public PEDecoder // Refcount above images. void AddRef(); ULONG Release(); - const SString& GetPath(); void ApplyBaseRelocations(); @@ -76,12 +73,10 @@ class PEImageLayout : public PEDecoder Volatile m_refCount; public: PEImage* m_pOwner; - DWORD m_Layout; }; typedef ReleaseHolder PEImageLayoutHolder; - //RawImageView is built on external data, does not need cleanup class RawImageLayout: public PEImageLayout { @@ -94,9 +89,10 @@ class RawImageLayout: public PEImageLayout public: RawImageLayout(const void *flat, COUNT_T size,PEImage* pOwner); - RawImageLayout(const void *mapped, PEImage* pOwner, BOOL bTakeOwnerShip, BOOL bFixedUp); }; +class FlatImageLayout; + // ConvertedImageView is for the case when we manually layout a flat image class ConvertedImageLayout: public PEImageLayout { @@ -106,7 +102,7 @@ class ConvertedImageLayout: public PEImageLayout CLRMapViewHolder m_FileView; public: #ifndef DACCESS_COMPILE - ConvertedImageLayout(PEImageLayout* source, BOOL isInBundle = FALSE); + ConvertedImageLayout(FlatImageLayout* source, BOOL isInBundle = FALSE); virtual ~ConvertedImageLayout(); #endif private: @@ -127,7 +123,10 @@ class MappedImageLayout: public PEImageLayout public: #ifndef DACCESS_COMPILE MappedImageLayout(PEImage* pOwner); + virtual ~MappedImageLayout(); #endif +private: + PT_RUNTIME_FUNCTION m_pExceptionDir; }; #if !defined(TARGET_UNIX) @@ -138,7 +137,9 @@ class LoadedImageLayout: public PEImageLayout HINSTANCE m_Module; public: #ifndef DACCESS_COMPILE - LoadedImageLayout(PEImage* pOwner, BOOL bNTSafeLoad, HRESULT* returnDontThrow); + LoadedImageLayout(PEImage* pOwner, HRESULT* returnDontThrow); + LoadedImageLayout(PEImage* pOwner, HMODULE hModule); + ~LoadedImageLayout() { CONTRACTL @@ -160,9 +161,10 @@ class FlatImageLayout: public PEImageLayout VPTR_VTABLE_CLASS(FlatImageLayout,PEImageLayout) VPTR_UNIQUE(0x59) protected: - HandleHolder m_FileMap; CLRMapViewHolder m_FileView; public: + HandleHolder m_FileMap; + #ifndef DACCESS_COMPILE FlatImageLayout(PEImage* pOwner); #endif diff --git a/src/coreclr/vm/peimagelayout.inl b/src/coreclr/vm/peimagelayout.inl index 17cebf77398d70..8c02d9cc6b764e 100644 --- a/src/coreclr/vm/peimagelayout.inl +++ b/src/coreclr/vm/peimagelayout.inl @@ -9,12 +9,6 @@ #include "util.hpp" #include "peimage.h" -inline const SString &PEImageLayout::GetPath() -{ - LIMITED_METHOD_CONTRACT; - return m_pOwner?m_pOwner->GetPath():SString::Empty(); -} - inline void PEImageLayout::AddRef() { CONTRACT_VOID From 01782942710fdfc635582252cd6b287f52624027 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:30:18 -0800 Subject: [PATCH 02/14] removed RawImageLayout --- src/coreclr/inc/vptr_list.h | 1 - src/coreclr/vm/peimage.cpp | 4 +- src/coreclr/vm/peimage.h | 2 +- src/coreclr/vm/peimagelayout.cpp | 91 ++++++++++++++++---------------- src/coreclr/vm/peimagelayout.h | 17 +----- 5 files changed, 50 insertions(+), 65 deletions(-) diff --git a/src/coreclr/inc/vptr_list.h b/src/coreclr/inc/vptr_list.h index 4f8baccd5cd687..03e0ee2ed11a10 100644 --- a/src/coreclr/inc/vptr_list.h +++ b/src/coreclr/inc/vptr_list.h @@ -41,7 +41,6 @@ VPTR_CLASS(TailCallStubManager) VPTR_CLASS(CallCountingStubManager) VPTR_CLASS(PEAssembly) VPTR_CLASS(PEImageLayout) -VPTR_CLASS(RawImageLayout) VPTR_CLASS(ConvertedImageLayout) VPTR_CLASS(MappedImageLayout) #if !defined(TARGET_UNIX) diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index 9ed92be8f40baf..758cd934a8c526 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -916,7 +916,7 @@ PTR_PEImageLayout PEImage::CreateLayoutFlat() } /* static */ -PTR_PEImage PEImage::CreateFromByteArray(const BYTE* flat, COUNT_T size) +PTR_PEImage PEImage::CreateFromByteArray(const BYTE* array, COUNT_T size) { CONTRACT(PTR_PEImage) { @@ -925,7 +925,7 @@ PTR_PEImage PEImage::CreateFromByteArray(const BYTE* flat, COUNT_T size) CONTRACT_END; PEImageHolder pImage(new PEImage()); - PTR_PEImageLayout pLayout = PEImageLayout::CreateFlat(flat,size,pImage); + PTR_PEImageLayout pLayout = PEImageLayout::CreateFromByteArray(pImage, array, size); _ASSERTE(!pLayout->IsMapped()); SimpleWriteLockHolder lock(pImage->m_pLayoutLock); diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index 9e8d7c9ae0414d..639ae746c014bc 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -73,7 +73,7 @@ class PEImage final ULONG Release(); #ifndef DACCESS_COMPILE - static PTR_PEImage CreateFromByteArray(const BYTE* flat, COUNT_T size); + static PTR_PEImage CreateFromByteArray(const BYTE* array, COUNT_T size); #ifndef TARGET_UNIX static PTR_PEImage CreateFromHMODULE(HMODULE hMod); #endif // !TARGET_UNIX diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 1350675ba65ed8..19106a6f0a10c2 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -21,10 +21,10 @@ extern "C" #endif #ifndef DACCESS_COMPILE -PEImageLayout* PEImageLayout::CreateFlat(const void *flat, COUNT_T size,PEImage* pOwner) +PEImageLayout* PEImageLayout::CreateFromByteArray(PEImage* pOwner, const BYTE* array, COUNT_T size) { STANDARD_VM_CONTRACT; - return new RawImageLayout(flat,size,pOwner); + return new FlatImageLayout(pOwner, array, size); } #ifndef TARGET_UNIX @@ -348,50 +348,6 @@ void PEImageLayout::ApplyBaseRelocations() } } - -RawImageLayout::RawImageLayout(const void *flat, COUNT_T size, PEImage* pOwner) -{ - CONTRACTL - { - CONSTRUCTOR_CHECK; - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - m_pOwner=pOwner; - - if (size) - { -#if defined(TARGET_WINDOWS) - if (Amsi::IsBlockedByAmsiScan((void*)flat, size)) - { - // This is required to throw a BadImageFormatException for compatibility, but - // use the message from ERROR_VIRUS_INFECTED to give better insight on what's wrong - SString virusHrString; - GetHRMsg(HRESULT_FROM_WIN32(ERROR_VIRUS_INFECTED), virusHrString); - ThrowHR(COR_E_BADIMAGEFORMAT, virusHrString); - } -#endif // defined(TARGET_WINDOWS) - - HandleHolder mapping(WszCreateFileMapping(INVALID_HANDLE_VALUE, - NULL, - PAGE_READWRITE, - 0, - size, - NULL)); - if (mapping==NULL) - ThrowLastError(); - m_DataCopy.Assign(CLRMapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0)); - if(m_DataCopy==NULL) - ThrowLastError(); - memcpy(m_DataCopy,flat,size); - flat=m_DataCopy; - } - Init((void*)flat,size); -} - ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source, BOOL isInBundle) { CONTRACTL @@ -780,6 +736,49 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner) Init(addr, (COUNT_T)size); } +FlatImageLayout::FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T size) +{ + CONTRACTL + { + CONSTRUCTOR_CHECK; + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + m_pOwner = pOwner; + + if (size == 0) + { + Init((void*)array, size); + } + else + { +#if defined(TARGET_WINDOWS) + if (Amsi::IsBlockedByAmsiScan((void*)array, size)) + { + // This is required to throw a BadImageFormatException for compatibility, but + // use the message from ERROR_VIRUS_INFECTED to give better insight on what's wrong + SString virusHrString; + GetHRMsg(HRESULT_FROM_WIN32(ERROR_VIRUS_INFECTED), virusHrString); + ThrowHR(COR_E_BADIMAGEFORMAT, virusHrString); + } +#endif // defined(TARGET_WINDOWS) + + m_FileMap.Assign(WszCreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, NULL)); + if (m_FileMap == NULL) + ThrowLastError(); + + m_FileView.Assign(CLRMapViewOfFile(m_FileMap, FILE_MAP_ALL_ACCESS, 0, 0, 0)); + if (m_FileView == NULL) + ThrowLastError(); + + memcpy(m_FileView, array, size); + Init((void*)m_FileView, size); + } +} + NativeImageLayout::NativeImageLayout(LPCWSTR fullPath) { PVOID loadedImage; diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index 4804e6a127622f..ccb3627ab8b122 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -44,7 +44,7 @@ class PEImageLayout : public PEDecoder public: #ifndef DACCESS_COMPILE - static PEImageLayout* CreateFlat(const void *flat, COUNT_T size,PEImage* pOwner); + static PEImageLayout* CreateFromByteArray(PEImage* pOwner, const BYTE* array, COUNT_T size); #ifndef TARGET_UNIX static PEImageLayout* CreateFromHMODULE(HMODULE hModule,PEImage* pOwner); #endif @@ -77,20 +77,6 @@ class PEImageLayout : public PEDecoder typedef ReleaseHolder PEImageLayoutHolder; -//RawImageView is built on external data, does not need cleanup -class RawImageLayout: public PEImageLayout -{ - VPTR_VTABLE_CLASS(RawImageLayout,PEImageLayout) -protected: - CLRMapViewHolder m_DataCopy; -#ifndef TARGET_UNIX - HModuleHolder m_LibraryHolder; -#endif // !TARGET_UNIX - -public: - RawImageLayout(const void *flat, COUNT_T size,PEImage* pOwner); -}; - class FlatImageLayout; // ConvertedImageView is for the case when we manually layout a flat image @@ -167,6 +153,7 @@ class FlatImageLayout: public PEImageLayout #ifndef DACCESS_COMPILE FlatImageLayout(PEImage* pOwner); + FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T size); #endif }; From 49c759fbbf85eeffe20a2b14474f6cb94c0f8185 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Sat, 27 Nov 2021 08:02:30 -0800 Subject: [PATCH 03/14] delete MappedImageLayout --- src/coreclr/inc/pedecoder.h | 4 +- src/coreclr/inc/vptr_list.h | 5 +- src/coreclr/pal/src/map/map.cpp | 7 +- src/coreclr/utilcode/pedecoder.cpp | 80 ------ src/coreclr/vm/peimage.cpp | 8 +- src/coreclr/vm/peimage.h | 4 +- src/coreclr/vm/peimagelayout.cpp | 377 ++++++++++++----------------- src/coreclr/vm/peimagelayout.h | 46 +--- 8 files changed, 184 insertions(+), 347 deletions(-) diff --git a/src/coreclr/inc/pedecoder.h b/src/coreclr/inc/pedecoder.h index 58b2ca13ef74b6..7479f685b46bc2 100644 --- a/src/coreclr/inc/pedecoder.h +++ b/src/coreclr/inc/pedecoder.h @@ -237,8 +237,6 @@ class PEDecoder BOOL IsILOnly() const; CHECK CheckILOnly() const; - void LayoutILOnly(void *base, bool enableExecution) const; - // Strong name & hashing support BOOL HasStrongNameSignature() const; @@ -348,6 +346,7 @@ class PEDecoder IMAGE_SECTION_HEADER *OffsetToSection(COUNT_T fileOffset) const; void SetRelocated(); + IMAGE_NT_HEADERS* FindNTHeaders() const; private: @@ -364,7 +363,6 @@ class PEDecoder static PTR_IMAGE_SECTION_HEADER FindFirstSection(IMAGE_NT_HEADERS * pNTHeaders); - IMAGE_NT_HEADERS *FindNTHeaders() const; IMAGE_COR20_HEADER *FindCorHeader() const; READYTORUN_HEADER *FindReadyToRunHeader() const; diff --git a/src/coreclr/inc/vptr_list.h b/src/coreclr/inc/vptr_list.h index 03e0ee2ed11a10..0fde8090d0699a 100644 --- a/src/coreclr/inc/vptr_list.h +++ b/src/coreclr/inc/vptr_list.h @@ -40,13 +40,12 @@ VPTR_CLASS(TailCallStubManager) #endif VPTR_CLASS(CallCountingStubManager) VPTR_CLASS(PEAssembly) + VPTR_CLASS(PEImageLayout) VPTR_CLASS(ConvertedImageLayout) -VPTR_CLASS(MappedImageLayout) -#if !defined(TARGET_UNIX) VPTR_CLASS(LoadedImageLayout) -#endif // !TARGET_UNIX VPTR_CLASS(FlatImageLayout) + #ifdef FEATURE_COMINTEROP VPTR_CLASS(ComMethodFrame) VPTR_CLASS(ComPlusMethodFrame) diff --git a/src/coreclr/pal/src/map/map.cpp b/src/coreclr/pal/src/map/map.cpp index 21ef333d437082..97e0e28d720641 100644 --- a/src/coreclr/pal/src/map/map.cpp +++ b/src/coreclr/pal/src/map/map.cpp @@ -2165,9 +2165,14 @@ void * MAPMapPEFile(HANDLE hFile, off_t offset) goto done; } + // For compat reasons we always allow 32bit header. + // We may not get correct preferred ImageBase, but otherwise 32bit header is the same for our uses here. + // We will not patch the header into platform bitness though, since coreclr does not require that. + // NOTE: actual checks whether the PE is platform-specific or contains native code will happen later as needed. if ((VAL16(IMAGE_DOS_SIGNATURE) != VAL16(dosHeader.e_magic)) || (VAL32(IMAGE_NT_SIGNATURE) != VAL32(ntHeader.Signature)) - || (VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic) ) ) + || ((VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic)) && + (VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic)))) { ERROR_(LOADER)( "Magic number mismatch\n" ); palError = ERROR_INVALID_PARAMETER; diff --git a/src/coreclr/utilcode/pedecoder.cpp b/src/coreclr/utilcode/pedecoder.cpp index c38e595223a3bd..1c635dfb6a768b 100644 --- a/src/coreclr/utilcode/pedecoder.cpp +++ b/src/coreclr/utilcode/pedecoder.cpp @@ -1659,86 +1659,6 @@ CHECK PEDecoder::CheckILOnlyEntryPoint() const } #endif // TARGET_X86 -#ifndef DACCESS_COMPILE - -void PEDecoder::LayoutILOnly(void *base, bool enableExecution) const -{ - CONTRACT_VOID - { - INSTANCE_CHECK; - PRECONDITION(CheckZeroedMemory(base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfImage))); - // Ideally we would require the layout address to honor the section alignment constraints. - // However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this - // case, we can only guarantee OS page alignment (which after all, is good enough.) - PRECONDITION(CheckAligned((SIZE_T)base, GetOsPageSize())); - THROWS; - GC_NOTRIGGER; - } - CONTRACT_END; - - // We're going to copy everything first, and write protect what we need to later. - - // First, copy headers - CopyMemory(base, (void *)m_base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders)); - - // Now, copy all sections to appropriate virtual address - - IMAGE_SECTION_HEADER *sectionStart = IMAGE_FIRST_SECTION(FindNTHeaders()); - IMAGE_SECTION_HEADER *sectionEnd = sectionStart + VAL16(FindNTHeaders()->FileHeader.NumberOfSections); - - IMAGE_SECTION_HEADER *section = sectionStart; - while (section < sectionEnd) - { - // Raw data may be less than section size if tail is zero, but may be more since VirtualSize is - // not padded. - DWORD size = min(VAL32(section->SizeOfRawData), VAL32(section->Misc.VirtualSize)); - - CopyMemory((BYTE *) base + VAL32(section->VirtualAddress), (BYTE *) m_base + VAL32(section->PointerToRawData), size); - - // Note that our memory is zeroed already, so no need to initialize any tail. - - section++; - } - - // Apply write protection to copied headers - DWORD oldProtection; - if (!ClrVirtualProtect((void *) base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders), - PAGE_READONLY, &oldProtection)) - ThrowLastError(); - - // Finally, apply proper protection to copied sections - for (section = sectionStart; section < sectionEnd; section++) - { - // Add appropriate page protection. - DWORD newProtection; - if (!enableExecution) - { - if (section->Characteristics & IMAGE_SCN_MEM_WRITE) - continue; - - newProtection = PAGE_READONLY; - } - else - { - newProtection = section->Characteristics & IMAGE_SCN_MEM_EXECUTE ? - PAGE_EXECUTE_READ : - section->Characteristics & IMAGE_SCN_MEM_WRITE ? - PAGE_READWRITE : - PAGE_READONLY; - } - - if (!ClrVirtualProtect((void*)((BYTE*)base + VAL32(section->VirtualAddress)), - VAL32(section->Misc.VirtualSize), - newProtection, &oldProtection)) - { - ThrowLastError(); - } - } - - RETURN; -} - -#endif // #ifndef DACCESS_COMPILE bool ReadResourceDirectoryHeader(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rva, IMAGE_RESOURCE_DIRECTORY_ENTRY** ppDirectoryEntries, IMAGE_RESOURCE_DIRECTORY **ppResourceDirectory) { diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index 758cd934a8c526..b64f60a8445cf0 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -838,13 +838,13 @@ PTR_PEImageLayout PEImage::GetOrCreateLayoutInternal(DWORD imageLayoutMask) if (bIsLoadedLayoutPreferred) { _ASSERTE(bIsLoadedLayoutSuitable); - pRetVal = PEImage::CreateLayoutMapped(!bIsFlatLayoutSuitable); + pRetVal = PEImage::CreateLoadedLayout(!bIsFlatLayoutSuitable); } if (pRetVal == NULL) { _ASSERTE(bIsFlatLayoutSuitable); - pRetVal = PEImage::CreateLayoutFlat(); + pRetVal = PEImage::CreateFlatLayout(); _ASSERTE(pRetVal != NULL); } } @@ -854,7 +854,7 @@ PTR_PEImageLayout PEImage::GetOrCreateLayoutInternal(DWORD imageLayoutMask) return pRetVal; } -PTR_PEImageLayout PEImage::CreateLayoutMapped(bool throwOnFailure) +PTR_PEImageLayout PEImage::CreateLoadedLayout(bool throwOnFailure) { CONTRACTL { @@ -889,7 +889,7 @@ PTR_PEImageLayout PEImage::CreateLayoutMapped(bool throwOnFailure) return pLoadLayout; } -PTR_PEImageLayout PEImage::CreateLayoutFlat() +PTR_PEImageLayout PEImage::CreateFlatLayout() { CONTRACTL { diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index 639ae746c014bc..2857bfdb1622f6 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -156,10 +156,10 @@ class PEImage final PTR_PEImageLayout GetOrCreateLayoutInternal(DWORD imageLayoutMask); // Create the mapped layout - PTR_PEImageLayout CreateLayoutMapped(bool throwOnFailure); + PTR_PEImageLayout CreateLoadedLayout(bool throwOnFailure); // Create the flat layout - PTR_PEImageLayout CreateLayoutFlat(); + PTR_PEImageLayout CreateFlatLayout(); void SetLayout(DWORD dwLayout, PTR_PEImageLayout pLayout); #endif diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 19106a6f0a10c2..d0e22463b8d9fe 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -59,7 +59,7 @@ PEImageLayout* PEImageLayout::CreateFromHMODULE(HMODULE hModule, PEImage* pOwner } #endif -PEImageLayout* PEImageLayout::LoadConverted(PEImage* pOwner, BOOL isInBundle) +PEImageLayout* PEImageLayout::LoadConverted(PEImage* pOwner) { STANDARD_VM_CONTRACT; @@ -67,33 +67,40 @@ PEImageLayout* PEImageLayout::LoadConverted(PEImage* pOwner, BOOL isInBundle) if (!pFlat->CheckFormat()) ThrowHR(COR_E_BADIMAGEFORMAT); - return new ConvertedImageLayout(pFlat, isInBundle); -} - -PEImageLayout* PEImageLayout::Load(PEImage* pOwner, HRESULT* loadFailure) -{ - STANDARD_VM_CONTRACT; - -#if defined(TARGET_UNIX) - return PEImageLayout::Map(pOwner); -#else - if (pOwner->IsInBundle()) + if (!pFlat->HasNTHeaders() || !pFlat->HasCorHeader() || !pFlat->IsILOnly()) { - return PEImageLayout::LoadConverted(pOwner, true); + return NULL; } - // TODO: VS this is always false, just to keep mapped layout alive for now (DAC failures) - if (!pOwner->IsFile()) + bool enableExecution = pFlat->HasReadyToRunHeader() && pFlat->IsNativeMachineFormat() && g_fAllowNativeImages; + if (!enableExecution) { - return PEImageLayout::Map(pOwner); + return pFlat.Extract(); } - PEImageLayoutHolder pAlloc(new LoadedImageLayout(pOwner, loadFailure)); - if (pAlloc->GetBase()==NULL) - return NULL; + return new ConvertedImageLayout(pFlat); +} - return pAlloc.Extract(); -#endif +PEImageLayout* PEImageLayout::Load(PEImage* pOwner, HRESULT* loadFailure) +{ + STANDARD_VM_CONTRACT; + +// TODO: VS HACK HACK + +// if (!pOwner->IsInBundle() +//#if defined(TARGET_UNIX) +// || (pOwner->GetUncompressedSize() == 0) +//#endif +// ) +// { +// PEImageLayoutHolder pAlloc(new LoadedImageLayout(pOwner, loadFailure)); +// if (pAlloc->GetBase() == NULL) +// return NULL; +// +// return pAlloc.Extract(); +// } + + return PEImageLayout::LoadConverted(pOwner); } PEImageLayout* PEImageLayout::LoadFlat(PEImage* pOwner) @@ -108,37 +115,6 @@ PEImageLayout *PEImageLayout::LoadNative(LPCWSTR fullPath) return new NativeImageLayout(fullPath); } -PEImageLayout* PEImageLayout::Map(PEImage* pOwner) -{ - CONTRACT(PEImageLayout*) - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pOwner)); - POSTCONDITION(CheckPointer(RETVAL)); - POSTCONDITION(RETVAL->CheckFormat()); - } - CONTRACT_END; - - PEImageLayoutHolder pAlloc = pOwner->GetUncompressedSize() > 0 ? - LoadConverted(pOwner, /* isInBundle */ true): - new MappedImageLayout(pOwner); - - if (pAlloc->GetBase()==NULL) - { - //cross-platform or a bad image - pAlloc = LoadConverted(pOwner); - } - else - { - if (!pAlloc->CheckFormat()) - ThrowHR(COR_E_BADIMAGEFORMAT); - } - - RETURN pAlloc.Extract(); -} - #ifdef TARGET_UNIX DWORD SectionCharacteristicsToPageProtection(UINT characteristics) { @@ -348,7 +324,7 @@ void PEImageLayout::ApplyBaseRelocations() } } -ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source, BOOL isInBundle) +ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source) { CONTRACTL { @@ -366,23 +342,13 @@ ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source, BOOL isInBun LOG((LF_LOADER, LL_INFO100, "PEImage: Opening manually mapped stream\n")); - // in bundle we may want to enable execution if the image contains R2R sections - // so must ensure the mapping is compatible with that - bool enableExecution = - source->HasCorHeader() && - source->HasReadyToRunHeader() && - g_fAllowNativeImages; - DWORD mapAccess = PAGE_READWRITE; DWORD viewAccess = FILE_MAP_ALL_ACCESS; #if !defined(TARGET_UNIX) - if (enableExecution) - { - // to make sections executable on Windows the view must have EXECUTE permissions - mapAccess = PAGE_EXECUTE_READWRITE; - viewAccess = FILE_MAP_EXECUTE | FILE_MAP_WRITE; - } + // to make sections executable on Windows the view must have EXECUTE permissions + mapAccess = PAGE_EXECUTE_READWRITE; + viewAccess = FILE_MAP_EXECUTE | FILE_MAP_WRITE; #endif m_FileMap.Assign(WszCreateFileMapping(INVALID_HANDLE_VALUE, NULL, @@ -400,54 +366,34 @@ ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source, BOOL isInBun if (m_FileView == NULL) ThrowLastError(); - source->LayoutILOnly(m_FileView, enableExecution); + source->LayoutILOnly(m_FileView); + IfFailThrow(Init(m_FileView)); - if (enableExecution) - { - if (!IsNativeMachineFormat()) - ThrowHR(COR_E_BADIMAGEFORMAT); + if (!IsNativeMachineFormat()) + ThrowHR(COR_E_BADIMAGEFORMAT); - // Do base relocation for PE, if necessary. - // otherwise R2R will be disabled for this image. - ApplyBaseRelocations(); + // Do base relocation for PE, if necessary. + // otherwise R2R will be disabled for this image. + ApplyBaseRelocations(); - // Check if there is a static function table and install it. (Windows only, except x86) + // Check if there is a static function table and install it. (Windows only, except x86) #if !defined(TARGET_UNIX) && !defined(TARGET_X86) - COUNT_T cbSize = 0; - PT_RUNTIME_FUNCTION pExceptionDir = (PT_RUNTIME_FUNCTION)GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_EXCEPTION, &cbSize); - DWORD tableSize = cbSize / sizeof(T_RUNTIME_FUNCTION); - - if (pExceptionDir != NULL) - { - if (!RtlAddFunctionTable(pExceptionDir, tableSize, (DWORD64)this->GetBase())) - ThrowLastError(); - - m_pExceptionDir = pExceptionDir; - } -#endif //TARGET_X86 - } -} + COUNT_T cbSize = 0; + PT_RUNTIME_FUNCTION pExceptionDir = (PT_RUNTIME_FUNCTION)GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_EXCEPTION, &cbSize); + DWORD tableSize = cbSize / sizeof(T_RUNTIME_FUNCTION); -ConvertedImageLayout::~ConvertedImageLayout() -{ - CONTRACTL + if (pExceptionDir != NULL) { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; + if (!RtlAddFunctionTable(pExceptionDir, tableSize, (DWORD64)this->GetBase())) + ThrowLastError(); -#if !defined(TARGET_UNIX) && !defined(TARGET_X86) - if (m_pExceptionDir) - { - RtlDeleteFunctionTable(m_pExceptionDir); + m_pExceptionDir = pExceptionDir; } -#endif +#endif //TARGET_X86 } -MappedImageLayout::~MappedImageLayout() +ConvertedImageLayout::~ConvertedImageLayout() { CONTRACTL { @@ -465,166 +411,97 @@ MappedImageLayout::~MappedImageLayout() #endif } -MappedImageLayout::MappedImageLayout(PEImage* pOwner) +LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, HRESULT* loadFailure) { CONTRACTL { CONSTRUCTOR_CHECK; STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pOwner)); } CONTRACTL_END; - m_pOwner=pOwner; - m_pExceptionDir = NULL; - HANDLE hFile = pOwner->GetFileHandle(); - INT64 offset = pOwner->GetOffset(); + m_pOwner = pOwner; _ASSERTE(pOwner->GetUncompressedSize() == 0); - // If mapping was requested, try to do SEC_IMAGE mapping - LOG((LF_LOADER, LL_INFO100, "PEImage: Opening OS mapped %S (hFile %p)\n", (LPCWSTR) pOwner->GetPath(), hFile)); - #ifndef TARGET_UNIX _ASSERTE(!pOwner->IsInBundle()); - - // Let OS map file for us - - // This may fail on e.g. cross-platform (32/64) loads. - m_FileMap.Assign(WszCreateFileMapping(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL)); - if (m_FileMap == NULL) - { - - // Capture last error as it may get reset below. - - DWORD dwLastError = GetLastError(); - // There is no reflection-only load on CoreCLR and so we can always throw an error here. - // It is important on Windows Phone. All assemblies that we load must have SEC_IMAGE set - // so that the OS can perform signature verification. - if (pOwner->IsFile()) - { - EEFileLoadException::Throw(pOwner->GetPathForErrorMessages(), HRESULT_FROM_WIN32(dwLastError)); - } - else - { - // Throw generic exception. - ThrowWin32(dwLastError); - } - - return; - } - -#ifdef _DEBUG - // Force relocs by occuping the preferred base while the actual mapping is performed - CLRMapViewHolder forceRelocs; - if (PEDecoder::GetForceRelocs()) - { - forceRelocs.Assign(CLRMapViewOfFile(m_FileMap, 0, 0, 0, 0)); - } -#endif // _DEBUG - - m_FileView.Assign(CLRMapViewOfFile(m_FileMap, 0, 0, 0, 0)); - if (m_FileView == NULL) - ThrowLastError(); - IfFailThrow(Init((void *) m_FileView)); - - if (!IsNativeMachineFormat() && !IsI386()) + m_Module = CLRLoadLibraryEx(pOwner->GetPath(), NULL, GetLoadWithAlteredSearchPathFlag()); + if (m_Module == NULL) { - //can't rely on the image - Reset(); + // Fetch the HRESULT upfront before anybody gets a chance to corrupt it + *loadFailure = HRESULT_FROM_GetLastError(); return; } -#ifdef _DEBUG - if (forceRelocs != NULL) - { - forceRelocs.Release(); + IfFailThrow(Init(m_Module, true)); + LOG((LF_LOADER, LL_INFO1000, "PEImage: Opened HMODULE %S\n", (LPCWSTR)pOwner->GetPath())); - if (CheckNTHeaders()) { - // Reserve the space so nobody can use it. A potential bug is likely to - // result in a plain AV this way. It is not a good idea to use the original - // mapping for the reservation since since it would lock the file on the disk. - - // ignore any errors - ClrVirtualAlloc((void*)GetPreferredBase(), GetVirtualSize(), MEM_RESERVE, PAGE_NOACCESS); - } - } -#endif // _DEBUG - -#else //!TARGET_UNIX +#else + HANDLE hFile = pOwner->GetFileHandle(); + INT64 offset = pOwner->GetOffset(); m_LoadedFile = PAL_LOADLoadPEFile(hFile, offset); - if (m_LoadedFile == NULL) + { + // Fetch the HRESULT upfront before anybody gets a chance to corrupt it + *loadFailure = HRESULT_FROM_GetLastError(); return; + } LOG((LF_LOADER, LL_INFO1000, "PEImage: image %S (hFile %p) mapped @ %p\n", - (LPCWSTR) pOwner->GetPath(), hFile, (void*)m_LoadedFile)); + (LPCWSTR)pOwner->GetPath(), hFile, (void*)m_LoadedFile)); - IfFailThrow(Init((void *) m_LoadedFile)); + IfFailThrow(Init((void*)m_LoadedFile)); if (!HasCorHeader()) - ThrowHR(COR_E_BADIMAGEFORMAT); - -#endif // !TARGET_UNIX + { + *loadFailure = COR_E_BADIMAGEFORMAT; + Reset(); + return; + } if (HasReadyToRunHeader() && g_fAllowNativeImages) { //Do base relocation for PE, if necessary. if (!IsNativeMachineFormat()) - ThrowHR(COR_E_BADIMAGEFORMAT); + { + *loadFailure = COR_E_BADIMAGEFORMAT; + Reset(); + return; + } ApplyBaseRelocations(); SetRelocated(); } - - // Check if there is a static function table and install it. (Windows only, except x86) -#if !defined(TARGET_UNIX) && !defined(TARGET_X86) - COUNT_T cbSize = 0; - PT_RUNTIME_FUNCTION pExceptionDir = (PT_RUNTIME_FUNCTION)GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_EXCEPTION, &cbSize); - DWORD tableSize = cbSize / sizeof(T_RUNTIME_FUNCTION); - - if (pExceptionDir != NULL) - { - if (!RtlAddFunctionTable(pExceptionDir, tableSize, (DWORD64)this->GetBase())) - ThrowLastError(); - - m_pExceptionDir = pExceptionDir; - } -#endif //TARGET_X86 +#endif } #if !defined(TARGET_UNIX) -LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, HRESULT* loadFailure) +LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, HMODULE hModule) +{ + m_pOwner = pOwner; + PEDecoder::Init((void*)hModule, /* relocated */ true); +} +#endif // !TARGET_UNIX + +LoadedImageLayout::~LoadedImageLayout() { CONTRACTL { - CONSTRUCTOR_CHECK; - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pOwner)); + NOTHROW; + GC_TRIGGERS; + MODE_ANY; } CONTRACTL_END; - m_pOwner=pOwner; - - DWORD dwFlags = GetLoadWithAlteredSearchPathFlag(); - m_Module = CLRLoadLibraryEx(pOwner->GetPath(), NULL, dwFlags); - if (m_Module == NULL) - { - // Fetch the HRESULT upfront before anybody gets a chance to corrupt it - HRESULT hr = HRESULT_FROM_GetLastError(); - *loadFailure = HRESULT_FROM_GetLastError(); - return; - } - IfFailThrow(Init(m_Module,true)); - - LOG((LF_LOADER, LL_INFO1000, "PEImage: Opened HMODULE %S\n", (LPCWSTR) pOwner->GetPath())); +#if !defined(TARGET_UNIX) + if (m_Module) + CLRFreeLibrary(m_Module); +#endif // !TARGET_UNIX } -LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, HMODULE hModule) -{ - m_pOwner = pOwner; - PEDecoder::Init((void*)hModule, /* relocated */ true); -} -#endif // !TARGET_UNIX + FlatImageLayout::FlatImageLayout(PEImage* pOwner) { @@ -779,6 +656,72 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T siz } } +void FlatImageLayout::LayoutILOnly(void* base) const +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + PRECONDITION(CheckZeroedMemory(base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfImage))); + // Ideally we would require the layout address to honor the section alignment constraints. + // However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this + // case, we can only guarantee OS page alignment (which after all, is good enough.) + PRECONDITION(CheckAligned((SIZE_T)base, GetOsPageSize())); + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + // We're going to copy everything first, and write protect what we need to later. + + // First, copy headers + CopyMemory(base, (void*)GetBase(), VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders)); + + // Now, copy all sections to appropriate virtual address + + IMAGE_SECTION_HEADER* sectionStart = IMAGE_FIRST_SECTION(FindNTHeaders()); + IMAGE_SECTION_HEADER* sectionEnd = sectionStart + VAL16(FindNTHeaders()->FileHeader.NumberOfSections); + + IMAGE_SECTION_HEADER* section = sectionStart; + while (section < sectionEnd) + { + // Raw data may be less than section size if tail is zero, but may be more since VirtualSize is + // not padded. + DWORD size = min(VAL32(section->SizeOfRawData), VAL32(section->Misc.VirtualSize)); + + CopyMemory((BYTE*)base + VAL32(section->VirtualAddress), (BYTE*)GetBase() + VAL32(section->PointerToRawData), size); + + // Note that our memory is zeroed already, so no need to initialize any tail. + + section++; + } + + // Apply write protection to copied headers + DWORD oldProtection; + if (!ClrVirtualProtect((void*)base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders), + PAGE_READONLY, &oldProtection)) + ThrowLastError(); + + // Finally, apply proper protection to copied sections + for (section = sectionStart; section < sectionEnd; section++) + { + // Add appropriate page protection. + DWORD newProtection = section->Characteristics & IMAGE_SCN_MEM_EXECUTE ? + PAGE_EXECUTE_READ : + section->Characteristics & IMAGE_SCN_MEM_WRITE ? + PAGE_READWRITE : + PAGE_READONLY; + + if (!ClrVirtualProtect((void*)((BYTE*)base + VAL32(section->VirtualAddress)), + VAL32(section->Misc.VirtualSize), + newProtection, &oldProtection)) + { + ThrowLastError(); + } + } + + RETURN; +} + NativeImageLayout::NativeImageLayout(LPCWSTR fullPath) { PVOID loadedImage; diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index ccb3627ab8b122..c8e7452ef393bb 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -50,9 +50,8 @@ class PEImageLayout : public PEDecoder #endif static PEImageLayout* Load(PEImage* pOwner, HRESULT* loadFailure); static PEImageLayout* LoadFlat(PEImage* pOwner); - static PEImageLayout* LoadConverted(PEImage* pOwner, BOOL isInBundle = FALSE); + static PEImageLayout* LoadConverted(PEImage* pOwner); static PEImageLayout* LoadNative(LPCWSTR fullPath); - static PEImageLayout* Map(PEImage* pOwner); #endif PEImageLayout(); virtual ~PEImageLayout(); @@ -88,59 +87,31 @@ class ConvertedImageLayout: public PEImageLayout CLRMapViewHolder m_FileView; public: #ifndef DACCESS_COMPILE - ConvertedImageLayout(FlatImageLayout* source, BOOL isInBundle = FALSE); + ConvertedImageLayout(FlatImageLayout* source); virtual ~ConvertedImageLayout(); #endif private: PT_RUNTIME_FUNCTION m_pExceptionDir; }; -class MappedImageLayout: public PEImageLayout +class LoadedImageLayout: public PEImageLayout { - VPTR_VTABLE_CLASS(MappedImageLayout,PEImageLayout) - VPTR_UNIQUE(0x15) + VPTR_VTABLE_CLASS(LoadedImageLayout,PEImageLayout) protected: #ifndef TARGET_UNIX - HandleHolder m_FileMap; - CLRMapViewHolder m_FileView; + HINSTANCE m_Module; #else PALPEFileHolder m_LoadedFile; #endif public: -#ifndef DACCESS_COMPILE - MappedImageLayout(PEImage* pOwner); - virtual ~MappedImageLayout(); -#endif -private: - PT_RUNTIME_FUNCTION m_pExceptionDir; -}; - -#if !defined(TARGET_UNIX) -class LoadedImageLayout: public PEImageLayout -{ - VPTR_VTABLE_CLASS(LoadedImageLayout,PEImageLayout) -protected: - HINSTANCE m_Module; -public: #ifndef DACCESS_COMPILE LoadedImageLayout(PEImage* pOwner, HRESULT* returnDontThrow); +#if !defined(TARGET_UNIX) LoadedImageLayout(PEImage* pOwner, HMODULE hModule); - - ~LoadedImageLayout() - { - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - if (m_Module) - CLRFreeLibrary(m_Module); - } +#endif // !TARGET_UNIX + ~LoadedImageLayout(); #endif // !DACCESS_COMPILE }; -#endif // !TARGET_UNIX class FlatImageLayout: public PEImageLayout { @@ -154,6 +125,7 @@ class FlatImageLayout: public PEImageLayout #ifndef DACCESS_COMPILE FlatImageLayout(PEImage* pOwner); FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T size); + void LayoutILOnly(void* base) const; #endif }; From 519fe5c9c677eba65cab1f337ee60e59170bda9f Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Wed, 1 Dec 2021 19:30:33 -0800 Subject: [PATCH 04/14] use mem-mapping on Windows --- src/coreclr/inc/pedecoder.inl | 2 - src/coreclr/pal/src/map/map.cpp | 7 +- .../ObjectWriter/R2RPEBuilder.cs | 12 +- src/coreclr/utilcode/pedecoder.cpp | 2 +- src/coreclr/vm/assembly.cpp | 25 +- src/coreclr/vm/assembly.hpp | 1 + src/coreclr/vm/domainfile.cpp | 1 + src/coreclr/vm/peimage.cpp | 43 +- src/coreclr/vm/peimage.h | 1 + src/coreclr/vm/peimage.inl | 9 + src/coreclr/vm/peimagelayout.cpp | 600 +++++++++++++++--- src/coreclr/vm/peimagelayout.h | 14 +- .../Bundle/TargetInfo.cs | 10 +- 13 files changed, 579 insertions(+), 148 deletions(-) diff --git a/src/coreclr/inc/pedecoder.inl b/src/coreclr/inc/pedecoder.inl index 8e463a3f3eba4f..44339248cbdbcb 100644 --- a/src/coreclr/inc/pedecoder.inl +++ b/src/coreclr/inc/pedecoder.inl @@ -100,7 +100,6 @@ inline PEDecoder::PEDecoder(PTR_VOID mappedBase, bool fixedUp /*= FALSE*/) { CONSTRUCTOR_CHECK; PRECONDITION(CheckPointer(mappedBase)); - PRECONDITION(CheckAligned(mappedBase, GetOsPageSize())); PRECONDITION(PEDecoder(mappedBase,fixedUp).CheckNTHeaders()); THROWS; GC_NOTRIGGER; @@ -172,7 +171,6 @@ inline HRESULT PEDecoder::Init(void *mappedBase, bool fixedUp /*= FALSE*/) NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(mappedBase)); - PRECONDITION(CheckAligned(mappedBase, GetOsPageSize())); PRECONDITION(!HasContents()); } CONTRACTL_END; diff --git a/src/coreclr/pal/src/map/map.cpp b/src/coreclr/pal/src/map/map.cpp index 97e0e28d720641..21ef333d437082 100644 --- a/src/coreclr/pal/src/map/map.cpp +++ b/src/coreclr/pal/src/map/map.cpp @@ -2165,14 +2165,9 @@ void * MAPMapPEFile(HANDLE hFile, off_t offset) goto done; } - // For compat reasons we always allow 32bit header. - // We may not get correct preferred ImageBase, but otherwise 32bit header is the same for our uses here. - // We will not patch the header into platform bitness though, since coreclr does not require that. - // NOTE: actual checks whether the PE is platform-specific or contains native code will happen later as needed. if ((VAL16(IMAGE_DOS_SIGNATURE) != VAL16(dosHeader.e_magic)) || (VAL32(IMAGE_NT_SIGNATURE) != VAL32(ntHeader.Signature)) - || ((VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic)) && - (VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic)))) + || (VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic) ) ) { ERROR_(LOADER)( "Magic number mismatch\n" ); palError = ERROR_INVALID_PARAMETER; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs index c7212368ace07a..898219266140af 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs @@ -683,17 +683,25 @@ public static PEHeaderBuilder Create(Subsystem subsystem, TargetDetails target) ulong imageBase = is64BitTarget ? PE64HeaderConstants.DllImageBase : PE32HeaderConstants.ImageBase; int fileAlignment = 0x200; - if (!target.IsWindows && !is64BitTarget) + if (target.IsWindows || !is64BitTarget) { // To minimize wasted VA space on 32-bit systems, align file to page boundaries (presumed to be 4K) + // + // On Windows we use 4K file alignment, so that we could load the PE manually, if needed, using + // placeholdr APIs (MapViewOfFile3, etc), which have 4K granularity. fileAlignment = 0x1000; } int sectionAlignment = 0x1000; if (!target.IsWindows && is64BitTarget) { - // On Linux, we must match the bottom 12 bits of section RVA's to their file offsets. For this reason + // On 64bit Linux, we must match the bottom 12 bits of section RVA's to their file offsets. For this reason // we need the same alignment for both. + // + // In addition to that we specify section RVAs to be at least 64K apart, which is > page on most systems. + // It ensures that the sections will not overlap when mapped from a singlefile bundle, which introduces a sub-page skew. + // + // Such format would not be accepted by OS loader on Windows, but it is not a problem on Unix. sectionAlignment = fileAlignment; } diff --git a/src/coreclr/utilcode/pedecoder.cpp b/src/coreclr/utilcode/pedecoder.cpp index 1c635dfb6a768b..c742250f5a5844 100644 --- a/src/coreclr/utilcode/pedecoder.cpp +++ b/src/coreclr/utilcode/pedecoder.cpp @@ -372,7 +372,7 @@ CHECK PEDecoder::CheckSection(COUNT_T previousAddressEnd, COUNT_T addressStart, CHECK(alignedSize >= VAL32(pNT->OptionalHeader.SizeOfImage)); // Check expected alignments - CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment))); + // CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment))); CHECK(CheckAligned(offsetStart, VAL32(pNT->OptionalHeader.FileAlignment))); CHECK(CheckAligned(offsetSize, VAL32(pNT->OptionalHeader.FileAlignment))); diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index 754cf91696ff7e..031ba832a9b32f 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -208,17 +208,6 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat // loading it entirely. //CacheFriendAssemblyInfo(); - if (IsCollectible()) - { - COUNT_T size; - BYTE *start = (BYTE*)m_pManifest->GetPEAssembly()->GetLoadedImageContents(&size); - if (start != NULL) - { - GCX_COOP(); - LoaderAllocator::AssociateMemoryWithLoaderAllocator(start, start + size, m_pLoaderAllocator); - } - } - { CANNOTTHROWCOMPLUSEXCEPTION(); FAULT_FORBID(); @@ -230,6 +219,20 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat } } +void Assembly::AssociateMemoryIfCollectible() +{ + if (IsCollectible()) + { + COUNT_T size; + BYTE* start = (BYTE*)m_pManifest->GetPEAssembly()->GetLoadedImageContents(&size); + if (start != NULL) + { + GCX_COOP(); + LoaderAllocator::AssociateMemoryWithLoaderAllocator(start, start + size, m_pLoaderAllocator); + } + } +} + Assembly::~Assembly() { CONTRACTL diff --git a/src/coreclr/vm/assembly.hpp b/src/coreclr/vm/assembly.hpp index f8de5c162e7524..0d8830b6205dee 100644 --- a/src/coreclr/vm/assembly.hpp +++ b/src/coreclr/vm/assembly.hpp @@ -81,6 +81,7 @@ class Assembly public: Assembly(BaseDomain *pDomain, PEAssembly *pPEAssembly, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible); void Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator); + void AssociateMemoryIfCollectible(); void StartUnload(); void Terminate( BOOL signalProfiler = TRUE ); diff --git a/src/coreclr/vm/domainfile.cpp b/src/coreclr/vm/domainfile.cpp index 6fae1b0e3375f2..b8f033637d784a 100644 --- a/src/coreclr/vm/domainfile.cpp +++ b/src/coreclr/vm/domainfile.cpp @@ -526,6 +526,7 @@ void DomainFile::LoadLibrary() CONTRACTL_END; GetPEAssembly()->EnsureLoaded(); + ((DomainAssembly*)this)->GetAssembly()->AssociateMemoryIfCollectible(); } void DomainFile::PostLoadLibrary() diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index b64f60a8445cf0..456a4051eed94d 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -814,13 +814,6 @@ PTR_PEImageLayout PEImage::GetOrCreateLayoutInternal(DWORD imageLayoutMask) if (pRetVal==NULL) { - // no-path layouts are filled up at creation, if image format permits. - if (!HasPath()) - { - // TODO: VS we should manually map flat layouts for R2R here. - ThrowHR(COR_E_BADIMAGEFORMAT); - } - BOOL bIsLoadedLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_LOADED) != 0); BOOL bIsFlatLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_FLAT) != 0); @@ -862,7 +855,6 @@ PTR_PEImageLayout PEImage::CreateLoadedLayout(bool throwOnFailure) GC_TRIGGERS; MODE_ANY; PRECONDITION(m_pLayoutLock->IsWriterLock()); - PRECONDITION(IsFile()); } CONTRACTL_END; @@ -901,17 +893,6 @@ PTR_PEImageLayout PEImage::CreateFlatLayout() PTR_PEImageLayout pFlatLayout = PEImageLayout::LoadFlat(this); SetLayout(IMAGE_FLAT, pFlatLayout); - - if (m_pLayouts[IMAGE_LOADED] == NULL && - pFlatLayout->CheckNTHeaders() && - pFlatLayout->CheckILOnly() && - !pFlatLayout->HasWriteableSections() && - !pFlatLayout->HasReadyToRunHeader()) - { - pFlatLayout->AddRef(); - SetLayout(IMAGE_LOADED, pFlatLayout); - } - return pFlatLayout; } @@ -930,16 +911,6 @@ PTR_PEImage PEImage::CreateFromByteArray(const BYTE* array, COUNT_T size) SimpleWriteLockHolder lock(pImage->m_pLayoutLock); pImage->SetLayout(IMAGE_FLAT,pLayout); - - // TODO: VS allow R2R for now, meaning it will run as IL-only - if (pLayout->CheckNTHeaders() && - pLayout->CheckILOnly() && - !pLayout->HasWriteableSections()) - { - pLayout->AddRef(); - pImage->SetLayout(IMAGE_LOADED, pLayout); - } - RETURN dac_cast(pImage.Extract()); } @@ -996,7 +967,12 @@ HANDLE PEImage::GetFileHandle() { ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS); m_hFile=WszCreateFile((LPCWSTR) GetPathToLoad(), - GENERIC_READ, + GENERIC_READ +#if TARGET_WINDOWS + // the file may have native code sections, make sure we have execute permissions + | GENERIC_EXECUTE +#endif + , FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, @@ -1027,7 +1003,12 @@ HRESULT PEImage::TryOpenFile() { ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); m_hFile=WszCreateFile((LPCWSTR)GetPathToLoad(), - GENERIC_READ, + GENERIC_READ +#if TARGET_WINDOWS + // the file may have native code sections, make sure we have execute permissions + | GENERIC_EXECUTE +#endif + , FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index 2857bfdb1622f6..3fbba2cba02539 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -91,6 +91,7 @@ class PEImage final BOOL HasLoadedLayout(); PTR_PEImageLayout GetLoadedLayout(); + PTR_PEImageLayout GetFlatLayout(); BOOL HasPath(); ULONG GetPathHash(); diff --git a/src/coreclr/vm/peimage.inl b/src/coreclr/vm/peimage.inl index fe6cdc050baf42..19668be89f97ce 100644 --- a/src/coreclr/vm/peimage.inl +++ b/src/coreclr/vm/peimage.inl @@ -150,6 +150,15 @@ inline PTR_PEImageLayout PEImage::GetLoadedLayout() return m_pLayouts[IMAGE_LOADED]; } +inline PTR_PEImageLayout PEImage::GetFlatLayout() +{ + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + _ASSERTE(m_pLayouts[IMAGE_FLAT] != NULL); + return m_pLayouts[IMAGE_FLAT]; +} + inline BOOL PEImage::IsOpened() { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index d0e22463b8d9fe..e0aeba9fccec56 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -63,18 +63,33 @@ PEImageLayout* PEImageLayout::LoadConverted(PEImage* pOwner) { STANDARD_VM_CONTRACT; - ReleaseHolder pFlat(new FlatImageLayout(pOwner)); - if (!pFlat->CheckFormat()) - ThrowHR(COR_E_BADIMAGEFORMAT); + _ASSERTE(!pOwner->HasLoadedLayout()); - if (!pFlat->HasNTHeaders() || !pFlat->HasCorHeader() || !pFlat->IsILOnly()) + ReleaseHolder pFlat; + if (pOwner->IsOpened()) { - return NULL; + pFlat = (FlatImageLayout*)pOwner->GetFlatLayout(); + pFlat->AddRef(); + } + else if (pOwner->IsFile()) + { + pFlat = new FlatImageLayout(pOwner); } - bool enableExecution = pFlat->HasReadyToRunHeader() && pFlat->IsNativeMachineFormat() && g_fAllowNativeImages; - if (!enableExecution) + if (pFlat == NULL || !pFlat->CheckILOnlyFormat()) + EEFileLoadException::Throw(pOwner->GetPathForErrorMessages(), COR_E_BADIMAGEFORMAT); + +#ifdef TARGET_UNIX + // we should not see R2R files here on Unix. + // ConvertedImageLayout may be able to handle them, but the fact that we were unable to + // load directly implies that MAPMapPEFile could not consume what crossgen produced. + // that is suspicious, one or another might have a bug. + _ASSERTE(!pFlat->HasReadyToRunHeader()); +#endif + + if (!pFlat->HasReadyToRunHeader() && !pFlat->HasWriteableSections()) { + // we can use flat layout for this return pFlat.Extract(); } @@ -85,20 +100,25 @@ PEImageLayout* PEImageLayout::Load(PEImage* pOwner, HRESULT* loadFailure) { STANDARD_VM_CONTRACT; -// TODO: VS HACK HACK - -// if (!pOwner->IsInBundle() -//#if defined(TARGET_UNIX) -// || (pOwner->GetUncompressedSize() == 0) -//#endif -// ) -// { -// PEImageLayoutHolder pAlloc(new LoadedImageLayout(pOwner, loadFailure)); -// if (pAlloc->GetBase() == NULL) -// return NULL; -// -// return pAlloc.Extract(); -// } + if (pOwner->IsFile()) + { + if (!pOwner->IsInBundle() +#if defined(TARGET_UNIX) + || (pOwner->GetUncompressedSize() == 0) +#endif + ) + { + PEImageLayoutHolder pAlloc(new LoadedImageLayout(pOwner, loadFailure)); + if (pAlloc->GetBase() != NULL) + return pAlloc.Extract(); + +#if TARGET_WINDOWS + // OS loader is always right. If a file cannot be loaded, do not try any further. + // Even if we may be able to load it, we do not want to support such files. + return NULL; +#endif + } + } return PEImageLayout::LoadConverted(pOwner); } @@ -150,7 +170,7 @@ DWORD SectionCharacteristicsToPageProtection(UINT characteristics) //To force base relocation on Vista (which uses ASLR), unmask IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE //(0x40) for OptionalHeader.DllCharacteristics -void PEImageLayout::ApplyBaseRelocations() +void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) { STANDARD_VM_CONTRACT; @@ -209,9 +229,6 @@ void PEImageLayout::ApplyBaseRelocations() // Restore the protection if (dwOldProtection != 0) { - BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ | - PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; - if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion, dwOldProtection, &dwOldProtection)) ThrowLastError(); @@ -230,7 +247,7 @@ void PEImageLayout::ApplyBaseRelocations() // Unprotect the section if it is not writable if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) == 0)) { - DWORD dwNewProtection = PAGE_READWRITE; + DWORD dwNewProtection = relocationMustWriteCopy ? PAGE_WRITECOPY : PAGE_READWRITE; #if defined(TARGET_UNIX) if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_EXECUTE)) != 0)) { @@ -324,6 +341,49 @@ void PEImageLayout::ApplyBaseRelocations() } } +static SIZE_T AllocatedPart(PVOID part) +{ + return (SIZE_T)part + 1; +} + +static SIZE_T MappedPart(PVOID part) +{ + return (SIZE_T)part; +} + +static PVOID PtrFromPart(SIZE_T part) +{ + return (PVOID)(part & ~1); +} + +static SIZE_T IsAllocatedPart(SIZE_T part) +{ + return part & 1; +} + +void ConvertedImageLayout::FreeImageParts() +{ + for (int i = 0; i < ConvertedImageLayout::MAX_PARTS; i++) + { + SIZE_T imagePart = this->m_imageParts[i]; + if (imagePart == 0) + break; + + // memory projected into placeholders is page-aligned. + // we are using "+1" to distinguish committed memory from mapped views, so that we know how to free them + if (IsAllocatedPart(imagePart)) + { + ClrVirtualFree(PtrFromPart(imagePart), 0, MEM_RELEASE); + } + else + { + CLRUnmapViewOfFile(PtrFromPart(imagePart)); + } + + this->m_imageParts[i] = NULL; + } +} + ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source) { CONTRACTL @@ -332,65 +392,63 @@ ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source) STANDARD_VM_CHECK; } CONTRACTL_END; - m_pOwner=source->m_pOwner; - _ASSERTE(!source->IsMapped()); + _ASSERTE(source->CheckILOnlyFormat()); + + m_pOwner = source->m_pOwner; m_pExceptionDir = NULL; + memset(m_imageParts, 0, sizeof(m_imageParts)); - if (!source->HasNTHeaders()) - EEFileLoadException::Throw(source->m_pOwner->GetPathForErrorMessages(), COR_E_BADIMAGEFORMAT); + bool relocationMustWriteCopy = false; + void* loadedImage = NULL; + // TODO: VS deal with LOG things. LOG((LF_LOADER, LL_INFO100, "PEImage: Opening manually mapped stream\n")); - DWORD mapAccess = PAGE_READWRITE; - DWORD viewAccess = FILE_MAP_ALL_ACCESS; - -#if !defined(TARGET_UNIX) - // to make sections executable on Windows the view must have EXECUTE permissions - mapAccess = PAGE_EXECUTE_READWRITE; - viewAccess = FILE_MAP_EXECUTE | FILE_MAP_WRITE; -#endif - - m_FileMap.Assign(WszCreateFileMapping(INVALID_HANDLE_VALUE, NULL, - mapAccess, 0, - source->GetVirtualSize(), NULL)); - - if (m_FileMap == NULL) - ThrowLastError(); - - m_FileView.Assign(CLRMapViewOfFile(m_FileMap, viewAccess, 0, 0, 0, - (void *) source->GetPreferredBase())); - if (m_FileView == NULL) - m_FileView.Assign(CLRMapViewOfFile(m_FileMap, viewAccess, 0, 0, 0)); - - if (m_FileView == NULL) - ThrowLastError(); +#ifdef TARGET_WINDOWS + loadedImage = source->LoadImageByMappingParts(this->m_imageParts); + if (loadedImage == NULL) + { + FreeImageParts(); + } + else + { + relocationMustWriteCopy = true; + } +#endif //TARGET_WINDOWS - source->LayoutILOnly(m_FileView); + if (loadedImage == NULL) + { + loadedImage = source->LoadImageByCopyingParts(this->m_imageParts); + } - IfFailThrow(Init(m_FileView)); + IfFailThrow(Init(loadedImage)); - if (!IsNativeMachineFormat()) - ThrowHR(COR_E_BADIMAGEFORMAT); + if (IsNativeMachineFormat() && g_fAllowNativeImages) + { + // Do base relocation and exception hookup, if necessary. + // otherwise R2R will be disabled for this image. - // Do base relocation for PE, if necessary. - // otherwise R2R will be disabled for this image. - ApplyBaseRelocations(); + ApplyBaseRelocations(relocationMustWriteCopy); - // Check if there is a static function table and install it. (Windows only, except x86) + // Check if there is a static function table and install it. (Windows only, except x86) #if !defined(TARGET_UNIX) && !defined(TARGET_X86) - COUNT_T cbSize = 0; - PT_RUNTIME_FUNCTION pExceptionDir = (PT_RUNTIME_FUNCTION)GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_EXCEPTION, &cbSize); - DWORD tableSize = cbSize / sizeof(T_RUNTIME_FUNCTION); + COUNT_T cbSize = 0; + PT_RUNTIME_FUNCTION pExceptionDir = (PT_RUNTIME_FUNCTION)GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_EXCEPTION, &cbSize); + DWORD tableSize = cbSize / sizeof(T_RUNTIME_FUNCTION); - if (pExceptionDir != NULL) - { - if (!RtlAddFunctionTable(pExceptionDir, tableSize, (DWORD64)this->GetBase())) - ThrowLastError(); + if (pExceptionDir != NULL) + { + // the only native code that we expect here is from R2R images + _ASSERTE(HasReadyToRunHeader()); - m_pExceptionDir = pExceptionDir; - } + if (!RtlAddFunctionTable(pExceptionDir, tableSize, (DWORD64)this->GetBase())) + ThrowLastError(); + + m_pExceptionDir = pExceptionDir; + } #endif //TARGET_X86 + } } ConvertedImageLayout::~ConvertedImageLayout() @@ -403,6 +461,8 @@ ConvertedImageLayout::~ConvertedImageLayout() } CONTRACTL_END; + FreeImageParts(); + #if !defined(TARGET_UNIX) && !defined(TARGET_X86) if (m_pExceptionDir) { @@ -471,7 +531,8 @@ LoadedImageLayout::LoadedImageLayout(PEImage* pOwner, HRESULT* loadFailure) return; } - ApplyBaseRelocations(); + // Unix specifies write sharing at map time (i.e. MAP_PRIVATE implies writecopy). + ApplyBaseRelocations(/* relocationMustWriteCopy*/ false); SetRelocated(); } #endif @@ -501,8 +562,6 @@ LoadedImageLayout::~LoadedImageLayout() #endif // !TARGET_UNIX } - - FlatImageLayout::FlatImageLayout(PEImage* pOwner) { CONTRACTL @@ -535,7 +594,17 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner) // It's okay if resource files are length zero if (size > 0) { - m_FileMap.Assign(WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL)); + INT64 uncompressedSize = pOwner->GetUncompressedSize(); + + DWORD mapAccess = PAGE_READONLY; +#if !defined(TARGET_UNIX) + // to map sections into executable views on Windows the mapping must have EXECUTE permissions + if (uncompressedSize == 0) + { + mapAccess = PAGE_EXECUTE_READ; + } +#endif + m_FileMap.Assign(WszCreateFileMapping(hFile, NULL, mapAccess, 0, 0, NULL)); if (m_FileMap == NULL) ThrowLastError(); @@ -556,13 +625,13 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner) m_FileView.Assign(view); addr = (LPVOID)((size_t)view + offset - mapBegin); - INT64 uncompressedSize = pOwner->GetUncompressedSize(); if (uncompressedSize > 0) { #if defined(CORECLR_EMBEDDED) // The mapping we have just created refers to the region in the bundle that contains compressed data. // We will create another anonymous memory-only mapping and uncompress file there. // The flat image will refer to the anonymous mapping instead and we will release the original mapping. + HandleHolder anonMap = WszCreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, uncompressedSize >> 32, (DWORD)uncompressedSize, NULL); if (anonMap == NULL) ThrowLastError(); @@ -632,6 +701,8 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T siz } else { + DWORD mapAccess = PAGE_READWRITE; + #if defined(TARGET_WINDOWS) if (Amsi::IsBlockedByAmsiScan((void*)array, size)) { @@ -641,13 +712,17 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T siz GetHRMsg(HRESULT_FROM_WIN32(ERROR_VIRUS_INFECTED), virusHrString); ThrowHR(COR_E_BADIMAGEFORMAT, virusHrString); } + + // to map sections into executable views on Windows the mapping must have EXECUTE permissions + mapAccess = PAGE_EXECUTE_READWRITE; + #endif // defined(TARGET_WINDOWS) - m_FileMap.Assign(WszCreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, NULL)); + m_FileMap.Assign(WszCreateFileMapping(INVALID_HANDLE_VALUE, NULL, mapAccess, 0, size, NULL)); if (m_FileMap == NULL) ThrowLastError(); - m_FileView.Assign(CLRMapViewOfFile(m_FileMap, FILE_MAP_ALL_ACCESS, 0, 0, 0)); + m_FileView.Assign(CLRMapViewOfFile(m_FileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0)); if (m_FileView == NULL) ThrowLastError(); @@ -656,20 +731,31 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T siz } } -void FlatImageLayout::LayoutILOnly(void* base) const +void* FlatImageLayout::LoadImageByCopyingParts(SIZE_T* m_imageParts) const { - CONTRACT_VOID + CONTRACTL { INSTANCE_CHECK; - PRECONDITION(CheckZeroedMemory(base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfImage))); - // Ideally we would require the layout address to honor the section alignment constraints. - // However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this - // case, we can only guarantee OS page alignment (which after all, is good enough.) - PRECONDITION(CheckAligned((SIZE_T)base, GetOsPageSize())); THROWS; GC_NOTRIGGER; } - CONTRACT_END; + CONTRACTL_END; + + void* preferredBase = NULL; +#ifdef FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION + if (g_useDefaultBaseAddr) + { + preferredBase = (void*)source->GetPreferredBase(); + } +#endif // FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION + + COUNT_T allocSize = ALIGN_UP(this->GetVirtualSize(), g_SystemInfo.dwAllocationGranularity); + LPVOID base = ClrVirtualAlloc(preferredBase, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (base == NULL) + ThrowLastError(); + + // when loading by copying we have only one part to free. + m_imageParts[0] = AllocatedPart(base); // We're going to copy everything first, and write protect what we need to later. @@ -719,9 +805,347 @@ void FlatImageLayout::LayoutILOnly(void* base) const } } - RETURN; + return base; +} + +#if TARGET_WINDOWS + +// VirtualAlloc2 +typedef PVOID(WINAPI* VirtualAlloc2Fn)( + HANDLE Process, + PVOID BaseAddress, + SIZE_T Size, + ULONG AllocationType, + ULONG PageProtection, + MEM_EXTENDED_PARAMETER* ExtendedParameters, + ULONG ParameterCount); + +VirtualAlloc2Fn pVirtualAlloc2 = NULL; + +// MapViewOfFile3 +typedef PVOID(WINAPI* MapViewOfFile3Fn)( + HANDLE FileMapping, + HANDLE Process, + PVOID BaseAddress, + ULONG64 Offset, + SIZE_T ViewSize, + ULONG AllocationType, + ULONG PageProtection, + MEM_EXTENDED_PARAMETER* ExtendedParameters, + ULONG ParameterCount); + +MapViewOfFile3Fn pMapViewOfFile3 = NULL; + +static bool HavePlaceholderAPI() +{ + const MapViewOfFile3Fn INVALID_ADDRESS_CENTINEL = (MapViewOfFile3Fn)1; + if (pMapViewOfFile3 == INVALID_ADDRESS_CENTINEL) + { + return false; + } + + if (pMapViewOfFile3 == NULL) + { + HMODULE hm = LoadLibraryW(_T("kernelbase.dll")); + if (hm != NULL) + { + pVirtualAlloc2 = (VirtualAlloc2Fn)GetProcAddress(hm, "VirtualAlloc2"); + pMapViewOfFile3 = (MapViewOfFile3Fn)GetProcAddress(hm, "MapViewOfFile3"); + + FreeLibrary(hm); + } + + if (pMapViewOfFile3 == NULL || pVirtualAlloc2 == NULL) + { + pMapViewOfFile3 = INVALID_ADDRESS_CENTINEL; + return false; + } + } + + return true; +} + +static PVOID AllocPlaceholder(PVOID BaseAddress, SIZE_T Size) +{ + return pVirtualAlloc2( + ::GetCurrentProcess(), + BaseAddress, + Size, + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, + NULL, + 0); } +static PVOID MapIntoPlaceholder( + HANDLE FileMapping, + ULONG64 FromOffset, + PVOID ToAddress, + SIZE_T ViewSize, + ULONG PageProtection +) +{ + return pMapViewOfFile3(FileMapping, ::GetCurrentProcess(), ToAddress, FromOffset, ViewSize, MEM_REPLACE_PLACEHOLDER, PageProtection, NULL, 0); +} + +static PVOID CommitIntoPlaceholder( + PVOID ToAddress, + SIZE_T Size, + ULONG PageProtection +) +{ + return pVirtualAlloc2(::GetCurrentProcess(), ToAddress, Size, MEM_COMMIT | MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, PageProtection, NULL, 0); +} + +static PVOID SplitPlaceholder( + PVOID& placeholderStart, + PVOID placeholderEnd, + SIZE_T size +) +{ + _ASSERTE((char*)placeholderStart + size <= placeholderEnd); + + if ((char*)placeholderStart + size < placeholderEnd) + { + if (!VirtualFree(placeholderStart, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) + { + return NULL; + } + } + + PVOID result = placeholderStart; + placeholderStart = (char*)placeholderStart + size; + + return result; +} + +static SIZE_T OffsetWithinPage(SIZE_T addr) +{ + return addr & (GetOsPageSize() - 1); +} + +static SIZE_T RoundToPage(SIZE_T size, SIZE_T offset) +{ + size_t result = size + OffsetWithinPage(offset); + _ASSERTE(result >= size); + return ROUND_UP_TO_PAGE(result); +} + +void* FlatImageLayout::LoadImageByMappingParts(SIZE_T* m_imageParts) const +{ + CONTRACTL + { + INSTANCE_CHECK; + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + if (!HavePlaceholderAPI() || m_pOwner->GetUncompressedSize() != 0) + { + return NULL; + } + + _ASSERTE(HasNTHeaders()); + + // offset in m_FileMap is nonzero when the data is in a singlefile bundle. + SIZE_T offset = (SIZE_T)m_pOwner->GetOffset(); + int imagePartIndex = 0; + PVOID pReserved = NULL; + IMAGE_NT_HEADERS* ntHeader = FindNTHeaders(); + + if ((ntHeader->OptionalHeader.FileAlignment < GetOsPageSize()) && + (ntHeader->OptionalHeader.FileAlignment != ntHeader->OptionalHeader.SectionAlignment)) + { + goto UNSUPPORTED; + } + + if (this->GetSize() < GetOsPageSize() * 2) + { + goto UNSUPPORTED; + } + + SIZE_T preferredBase = ntHeader->OptionalHeader.ImageBase; + SIZE_T virtualSize = ntHeader->OptionalHeader.SizeOfImage; + SIZE_T reserveSize = RoundToPage(virtualSize, offset); + + PVOID usedBaseAddr = NULL; +#ifdef FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION + if (g_useDefaultBaseAddr) + { + usedBaseAddr = (PVOID)preferredBase; + } +#endif // FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION + + pReserved = AllocPlaceholder(usedBaseAddr, reserveSize); + if (pReserved == NULL) + { + _ASSERTE(!"FAILED"); + goto FAILED; + } + + PVOID reservedEnd = (char*)pReserved + reserveSize; + IMAGE_DOS_HEADER* loadedHeader = (IMAGE_DOS_HEADER*)((SIZE_T)pReserved + OffsetWithinPage(offset)); + _ASSERTE(OffsetWithinPage(offset) == OffsetWithinPage((SIZE_T)loadedHeader)); + + //first, map the PE header to the first page in the image. + SIZE_T dataStart = offset; + SIZE_T mapFrom = ROUND_DOWN_TO_PAGE(dataStart); + SIZE_T dataEnd = dataStart + ntHeader->OptionalHeader.SizeOfHeaders; + SIZE_T mapEnd = ROUND_UP_TO_PAGE(dataEnd); + SIZE_T mapSize = mapEnd - mapFrom; + PVOID pMapped = SplitPlaceholder(pReserved, reservedEnd, mapSize); + if (!pMapped) + { + _ASSERTE(!"FAILED"); + goto FAILED; + } + + pMapped = MapIntoPlaceholder(m_FileMap, mapFrom, pMapped, mapSize, PAGE_READONLY); + if (!pMapped) + { + _ASSERTE(!"FAILED"); + goto FAILED; + } + + m_imageParts[imagePartIndex++] = MappedPart(pMapped); + + //Get pointers to the section headers + IMAGE_SECTION_HEADER* firstSection = (IMAGE_SECTION_HEADER*)(((SIZE_T)loadedHeader) + + loadedHeader->e_lfanew + + offsetof(IMAGE_NT_HEADERS, OptionalHeader) + + VAL16(ntHeader->FileHeader.SizeOfOptionalHeader)); + + unsigned numSections = ntHeader->FileHeader.NumberOfSections; + if (numSections + 2 > ConvertedImageLayout::MAX_PARTS) + { + // too many sections. we do not expect this and do not want to handle here, but it is not an error. + _ASSERTE(!"too many sections"); + goto UNSUPPORTED; + } + + for (unsigned i = 0; i < numSections; ++i) + { + //for each section, map the section of the file to the correct virtual offset. + IMAGE_SECTION_HEADER& currentHeader = firstSection[i]; + SIZE_T sectionBase = (SIZE_T)loadedHeader + currentHeader.VirtualAddress; + SIZE_T sectionBaseAligned = ROUND_DOWN_TO_PAGE(sectionBase); + + if ((SIZE_T)pReserved > sectionBaseAligned) + { + // can't allow section mappings to overlap. + // this could happen with legacy bundles and sub-page aligned data + // we can't handle such cases here, but it is not an error. + _ASSERTE(!"can't allow section mappings to overlap"); + goto UNSUPPORTED; + } + + // Is there space between the previous section and this one? If so, split an unmapped placeholder to cover it. + if ((SIZE_T)pReserved < sectionBaseAligned) + { + SIZE_T holeSize = (SIZE_T)sectionBaseAligned - (SIZE_T)pReserved; + _ASSERTE((char*)pReserved + holeSize <= reservedEnd); + PVOID pHole = SplitPlaceholder(pReserved, reservedEnd, holeSize); + if (!pHole) + { + _ASSERTE(!"FAILED"); + goto FAILED; + } + + VirtualFree(pHole, 0, MEM_RELEASE); + } + + DWORD pageProtection = currentHeader.Characteristics & IMAGE_SCN_MEM_EXECUTE ? + PAGE_EXECUTE_READ : + currentHeader.Characteristics & IMAGE_SCN_MEM_WRITE ? + PAGE_WRITECOPY : + PAGE_READONLY; + + dataStart = offset + currentHeader.PointerToRawData; + mapFrom = ROUND_DOWN_TO_PAGE(dataStart); + dataEnd = dataStart + currentHeader.SizeOfRawData; + mapEnd = ROUND_UP_TO_PAGE(dataEnd); + + // the aligned end could extend beyond the end of the file. + // then map only the aligned chunk that fits, the rest we will copy. + while (mapEnd > offset + this->GetSize()) + { + mapEnd -= GetOsPageSize(); + } + + // if we have something to map at page granularity, map it + if (mapEnd > mapFrom) + { + mapSize = mapEnd - mapFrom; + _ASSERTE((char*)pReserved + mapSize <= reservedEnd); + pMapped = SplitPlaceholder(pReserved, reservedEnd, mapSize); + if (!pMapped) + { + _ASSERTE(!"FAILED"); + goto FAILED; + } + + pMapped = MapIntoPlaceholder(m_FileMap, mapFrom, pMapped, mapSize, pageProtection); + if (!pMapped) + { + _ASSERTE(!"FAILED"); + goto FAILED; + } + + m_imageParts[imagePartIndex++] = MappedPart(pMapped); + } + + // if we have something left, copy it + if (mapEnd < dataEnd) + { + SIZE_T toCopy = dataEnd - mapEnd; + SIZE_T toAllocate = ROUND_UP_TO_PAGE(toCopy); + _ASSERTE((char*)pReserved + toAllocate <= reservedEnd); + PVOID pAllocated = SplitPlaceholder(pReserved, reservedEnd, toAllocate); + if (!pAllocated) + { + _ASSERTE(!"FAILED"); + goto FAILED; + } + + pAllocated = CommitIntoPlaceholder(pAllocated, toAllocate, PAGE_READWRITE); + if (!pAllocated) + { + _ASSERTE(!"FAILED"); + goto FAILED; + } + + m_imageParts[imagePartIndex++] = AllocatedPart(pAllocated); + + CopyMemory(pAllocated, (char*)this->GetBase() + mapEnd - offset, toCopy); + VirtualProtect(pAllocated, toAllocate, pageProtection, &pageProtection); + } + } + + // Is there reserved space that we did not use? If so, release it. + if (pReserved < reservedEnd) + { + VirtualFree(pReserved, 0, MEM_RELEASE); + } + + return loadedHeader; + +FAILED: + _ASSERTE(!"FAILED"); + if (pReserved && pReserved < reservedEnd) + m_imageParts[imagePartIndex++] = AllocatedPart(pReserved); + + ThrowLastError(); + +UNSUPPORTED: + if (pReserved && pReserved < reservedEnd) + m_imageParts[imagePartIndex++] = AllocatedPart(pReserved); + + return NULL; +} + +#endif //TARGET_WINDOWS + NativeImageLayout::NativeImageLayout(LPCWSTR fullPath) { PVOID loadedImage; @@ -756,8 +1180,8 @@ NativeImageLayout::NativeImageLayout(LPCWSTR fullPath) #if TARGET_UNIX PEDecoder::Init(loadedImage, /* relocated */ false); - ApplyBaseRelocations(); - SetRelocated(); + // Unix specifies write sharing at map time (i.e. MAP_PRIVATE implies writecopy). + ApplyBaseRelocations(/* relocationMustWriteCopy*/ false); #else // TARGET_UNIX PEDecoder::Init(loadedImage, /* relocated */ true); #endif // TARGET_UNIX diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index c8e7452ef393bb..f3b68615d98460 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -61,7 +61,7 @@ class PEImageLayout : public PEDecoder void AddRef(); ULONG Release(); - void ApplyBaseRelocations(); + void ApplyBaseRelocations(bool relocationMustWriteCopy); public: #ifdef DACCESS_COMPILE @@ -82,16 +82,16 @@ class FlatImageLayout; class ConvertedImageLayout: public PEImageLayout { VPTR_VTABLE_CLASS(ConvertedImageLayout,PEImageLayout) -protected: - HandleHolder m_FileMap; - CLRMapViewHolder m_FileView; public: + static const int MAX_PARTS = 16; #ifndef DACCESS_COMPILE ConvertedImageLayout(FlatImageLayout* source); virtual ~ConvertedImageLayout(); + void FreeImageParts(); #endif private: PT_RUNTIME_FUNCTION m_pExceptionDir; + SIZE_T m_imageParts[MAX_PARTS]; }; class LoadedImageLayout: public PEImageLayout @@ -125,7 +125,11 @@ class FlatImageLayout: public PEImageLayout #ifndef DACCESS_COMPILE FlatImageLayout(PEImage* pOwner); FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T size); - void LayoutILOnly(void* base) const; + void* LoadImageByCopyingParts(SIZE_T* m_imageParts) const; + +#if TARGET_WINDOWS + void* LoadImageByMappingParts(SIZE_T* m_imageParts) const; +#endif #endif }; diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs index 2d3f6566a5ad5c..fff66eec51f7aa 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs @@ -64,10 +64,16 @@ public TargetInfo(OSPlatform? os, Architecture? arch, Version targetFrameworkVer // See https://github.com/dotnet/runtime/issues/41832. AssemblyAlignment = 4096; } + else if (IsWindows) + { + // We align assemblies in the bundle at 4K - per requirements of memory mapping API (MapViewOfFile3, etc). + // This is only necessary for R2R assemblies, but we do it for all assemblies for simplicity. + AssemblyAlignment = 4096; + } else { - // Otherwise, assemblies are 16 bytes aligned, so that their sections can be memory-mapped cache aligned. - AssemblyAlignment = 16; + // Otherwise, assemblies are 64 bytes aligned, so that their sections can be memory-mapped cache aligned. + AssemblyAlignment = 64; } } From 00ae715a80dcf9984edb9e167173f8688c7a37a5 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Thu, 9 Dec 2021 19:00:17 -0800 Subject: [PATCH 05/14] tweaks and touchups --- .../ObjectWriter/R2RPEBuilder.cs | 4 +-- src/coreclr/utilcode/pedecoder.cpp | 5 ++- src/coreclr/vm/assembly.cpp | 11 ++++--- src/coreclr/vm/assemblyname.cpp | 9 +++--- src/coreclr/vm/peassembly.cpp | 9 ++---- src/coreclr/vm/peimage.cpp | 31 ++++++++++--------- src/coreclr/vm/peimagelayout.h | 1 - .../Bundle/TargetInfo.cs | 2 +- 8 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs index 898219266140af..55d1054cbb6b4a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs @@ -687,8 +687,8 @@ public static PEHeaderBuilder Create(Subsystem subsystem, TargetDetails target) { // To minimize wasted VA space on 32-bit systems, align file to page boundaries (presumed to be 4K) // - // On Windows we use 4K file alignment, so that we could load the PE manually, if needed, using - // placeholdr APIs (MapViewOfFile3, etc), which have 4K granularity. + // On Windows we use 4K file alignment, per requirements of memory mapping API (MapViewOfFile3, et al). + // We also want files always accepted by the native loader, thus we can't use the same approach as on Unix. fileAlignment = 0x1000; } diff --git a/src/coreclr/utilcode/pedecoder.cpp b/src/coreclr/utilcode/pedecoder.cpp index c742250f5a5844..b5f5def348f265 100644 --- a/src/coreclr/utilcode/pedecoder.cpp +++ b/src/coreclr/utilcode/pedecoder.cpp @@ -372,7 +372,10 @@ CHECK PEDecoder::CheckSection(COUNT_T previousAddressEnd, COUNT_T addressStart, CHECK(alignedSize >= VAL32(pNT->OptionalHeader.SizeOfImage)); // Check expected alignments - // CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment))); +#if TARGET_WINDOWS + // R2R files on Unix do not align section RVAs + CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment))); +#endif CHECK(CheckAligned(offsetStart, VAL32(pNT->OptionalHeader.FileAlignment))); CHECK(CheckAligned(offsetSize, VAL32(pNT->OptionalHeader.FileAlignment))); diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index 031ba832a9b32f..e6167062add89d 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -225,11 +225,12 @@ void Assembly::AssociateMemoryIfCollectible() { COUNT_T size; BYTE* start = (BYTE*)m_pManifest->GetPEAssembly()->GetLoadedImageContents(&size); - if (start != NULL) - { - GCX_COOP(); - LoaderAllocator::AssociateMemoryWithLoaderAllocator(start, start + size, m_pLoaderAllocator); - } + + // We should have the content loaded at this time. There will be no other attempt to start tracking. + _ASSERTE(start != NULL); + + GCX_COOP(); + LoaderAllocator::AssociateMemoryWithLoaderAllocator(start, start + size, m_pLoaderAllocator); } } diff --git a/src/coreclr/vm/assemblyname.cpp b/src/coreclr/vm/assemblyname.cpp index 74fc7799bef008..bd6e41e52611b3 100644 --- a/src/coreclr/vm/assemblyname.cpp +++ b/src/coreclr/vm/assemblyname.cpp @@ -52,11 +52,10 @@ FCIMPL1(Object*, AssemblyNameNative::GetFileInformation, StringObject* filenameU SString sFileName(gc.filename->GetBuffer()); PEImageHolder pImage = PEImage::OpenImage(sFileName, MDInternalImport_NoCache); - // Load the temporary image using a flat layout, instead of - // waiting for it to happen during HasNTHeaders. This allows us to - // get the assembly name for images that contain native code for a - // non-native platform. - PEImageLayout* pLayout = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); + // Ask for FLAT. We will only look at metadata and release the image shortly. + // Besides we may be getting the assembly name for images that contain native code for a + // non-native platform and would end up using flat anyways. + PEImageLayout* pLayout = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_FLAT); pImage->VerifyIsAssembly(); diff --git a/src/coreclr/vm/peassembly.cpp b/src/coreclr/vm/peassembly.cpp index 01447537693206..9f228fdb5af825 100644 --- a/src/coreclr/vm/peassembly.cpp +++ b/src/coreclr/vm/peassembly.cpp @@ -461,12 +461,7 @@ void PEAssembly::GetEmbeddedResource(DWORD dwOffset, DWORD *cbResource, PBYTE *p } 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 = GetPEImage(); - PEImageLayout* theImage = image->GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); if (!theImage->CheckResource(dwOffset)) ThrowHR(COR_E_BADIMAGEFORMAT); @@ -697,8 +692,8 @@ PEAssembly::PEAssembly( { _ASSERTE(pPEImage->CheckUniqueInstance()); pPEImage->AddRef(); - - // We require a mapping for the file. + // We require an open layout for the file. + // Most likely we have one already, just make sure we have one. pPEImage->GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); m_PEImage = pPEImage; } diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index 456a4051eed94d..a40099e78f3d8d 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -819,7 +819,8 @@ PTR_PEImageLayout PEImage::GetOrCreateLayoutInternal(DWORD imageLayoutMask) BOOL bIsLoadedLayoutPreferred = !bIsFlatLayoutSuitable; -#if !defined(TARGET_UNIX) +#ifdef TARGET_WINDOWS + // on Windows we prefer to just load the file using OS loader if (!IsInBundle() && bIsLoadedLayoutSuitable) { bIsLoadedLayoutPreferred = TRUE; @@ -865,6 +866,8 @@ PTR_PEImageLayout PEImage::CreateLoadedLayout(bool throwOnFailure) if (pLoadLayout != NULL) { SetLayout(IMAGE_LOADED,pLoadLayout); + // loaded layout is functionally a superset of flat, + // so fill the flat slot, if not filled already. if (m_pLayouts[IMAGE_FLAT] == NULL) { pLoadLayout->AddRef(); @@ -930,22 +933,20 @@ PTR_PEImage PEImage::CreateFromHMODULE(HMODULE hMod) WszGetModuleFileName(hMod, path); PEImageHolder pImage(PEImage::OpenImage(path, MDInternalImport_Default)); - if (pImage->HasLoadedLayout()) + if (!pImage->HasLoadedLayout()) { - _ASSERTE(pImage->m_pLayouts[IMAGE_FLAT] != NULL); - RETURN dac_cast(pImage.Extract()); - } - - PTR_PEImageLayout pLayout = PEImageLayout::CreateFromHMODULE(hMod, pImage); + PTR_PEImageLayout pLayout = PEImageLayout::CreateFromHMODULE(hMod, pImage); - SimpleWriteLockHolder lock(pImage->m_pLayoutLock); - pImage->SetLayout(IMAGE_LOADED, pLayout); - if (pImage->m_pLayouts[IMAGE_FLAT] == NULL) - { - pLayout->AddRef(); - pImage->SetLayout(IMAGE_FLAT, pLayout); + SimpleWriteLockHolder lock(pImage->m_pLayoutLock); + pImage->SetLayout(IMAGE_LOADED, pLayout); + if (pImage->m_pLayouts[IMAGE_FLAT] == NULL) + { + pLayout->AddRef(); + pImage->SetLayout(IMAGE_FLAT, pLayout); + } } + _ASSERTE(pImage->m_pLayouts[IMAGE_FLAT] != NULL); RETURN dac_cast(pImage.Extract()); } #endif // !TARGET_UNIX @@ -969,7 +970,7 @@ HANDLE PEImage::GetFileHandle() m_hFile=WszCreateFile((LPCWSTR) GetPathToLoad(), GENERIC_READ #if TARGET_WINDOWS - // the file may have native code sections, make sure we have execute permissions + // the file may have native code sections, make sure we are allowed to execute the file | GENERIC_EXECUTE #endif , @@ -1005,7 +1006,7 @@ HRESULT PEImage::TryOpenFile() m_hFile=WszCreateFile((LPCWSTR)GetPathToLoad(), GENERIC_READ #if TARGET_WINDOWS - // the file may have native code sections, make sure we have execute permissions + // the file may have native code sections, make sure we are allowed to execute the file | GENERIC_EXECUTE #endif , diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index f3b68615d98460..1100a2085118af 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -126,7 +126,6 @@ class FlatImageLayout: public PEImageLayout FlatImageLayout(PEImage* pOwner); FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T size); void* LoadImageByCopyingParts(SIZE_T* m_imageParts) const; - #if TARGET_WINDOWS void* LoadImageByMappingParts(SIZE_T* m_imageParts) const; #endif diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs index fff66eec51f7aa..49313415c8e360 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs @@ -66,7 +66,7 @@ public TargetInfo(OSPlatform? os, Architecture? arch, Version targetFrameworkVer } else if (IsWindows) { - // We align assemblies in the bundle at 4K - per requirements of memory mapping API (MapViewOfFile3, etc). + // We align assemblies in the bundle at 4K - per requirements of memory mapping API (MapViewOfFile3, et al). // This is only necessary for R2R assemblies, but we do it for all assemblies for simplicity. AssemblyAlignment = 4096; } From 230e9efebd953b01a69d89f438ee5813a822e262 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Fri, 10 Dec 2021 00:16:08 -0800 Subject: [PATCH 06/14] a few cleanups --- src/coreclr/vm/peimagelayout.cpp | 40 ++++++++++---------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index e0aeba9fccec56..b7b9b27108e0ef 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -979,10 +979,7 @@ void* FlatImageLayout::LoadImageByMappingParts(SIZE_T* m_imageParts) const pReserved = AllocPlaceholder(usedBaseAddr, reserveSize); if (pReserved == NULL) - { - _ASSERTE(!"FAILED"); goto FAILED; - } PVOID reservedEnd = (char*)pReserved + reserveSize; IMAGE_DOS_HEADER* loadedHeader = (IMAGE_DOS_HEADER*)((SIZE_T)pReserved + OffsetWithinPage(offset)); @@ -996,17 +993,11 @@ void* FlatImageLayout::LoadImageByMappingParts(SIZE_T* m_imageParts) const SIZE_T mapSize = mapEnd - mapFrom; PVOID pMapped = SplitPlaceholder(pReserved, reservedEnd, mapSize); if (!pMapped) - { - _ASSERTE(!"FAILED"); goto FAILED; - } pMapped = MapIntoPlaceholder(m_FileMap, mapFrom, pMapped, mapSize, PAGE_READONLY); if (!pMapped) - { - _ASSERTE(!"FAILED"); goto FAILED; - } m_imageParts[imagePartIndex++] = MappedPart(pMapped); @@ -1017,7 +1008,8 @@ void* FlatImageLayout::LoadImageByMappingParts(SIZE_T* m_imageParts) const + VAL16(ntHeader->FileHeader.SizeOfOptionalHeader)); unsigned numSections = ntHeader->FileHeader.NumberOfSections; - if (numSections + 2 > ConvertedImageLayout::MAX_PARTS) + // in a worst case we need 2 parts for every section and a header + 1 for unused tail. + if ((numSections + 1) * 2 + 1 > ConvertedImageLayout::MAX_PARTS) { // too many sections. we do not expect this and do not want to handle here, but it is not an error. _ASSERTE(!"too many sections"); @@ -1043,16 +1035,17 @@ void* FlatImageLayout::LoadImageByMappingParts(SIZE_T* m_imageParts) const // Is there space between the previous section and this one? If so, split an unmapped placeholder to cover it. if ((SIZE_T)pReserved < sectionBaseAligned) { + // we can handle a hole between sections, but it may indicate a noncompliant R2R PE. + _ASSERTE(!"Hole between sections"); + SIZE_T holeSize = (SIZE_T)sectionBaseAligned - (SIZE_T)pReserved; _ASSERTE((char*)pReserved + holeSize <= reservedEnd); PVOID pHole = SplitPlaceholder(pReserved, reservedEnd, holeSize); if (!pHole) - { - _ASSERTE(!"FAILED"); goto FAILED; - } - VirtualFree(pHole, 0, MEM_RELEASE); + // can't MEM_RELEASE the unused part yet, since the image must be contiguous. (see AssociateMemoryWithLoaderAllocator) + m_imageParts[imagePartIndex++] = AllocatedPart(pHole); } DWORD pageProtection = currentHeader.Characteristics & IMAGE_SCN_MEM_EXECUTE ? @@ -1080,17 +1073,11 @@ void* FlatImageLayout::LoadImageByMappingParts(SIZE_T* m_imageParts) const _ASSERTE((char*)pReserved + mapSize <= reservedEnd); pMapped = SplitPlaceholder(pReserved, reservedEnd, mapSize); if (!pMapped) - { - _ASSERTE(!"FAILED"); goto FAILED; - } pMapped = MapIntoPlaceholder(m_FileMap, mapFrom, pMapped, mapSize, pageProtection); if (!pMapped) - { - _ASSERTE(!"FAILED"); goto FAILED; - } m_imageParts[imagePartIndex++] = MappedPart(pMapped); } @@ -1103,17 +1090,11 @@ void* FlatImageLayout::LoadImageByMappingParts(SIZE_T* m_imageParts) const _ASSERTE((char*)pReserved + toAllocate <= reservedEnd); PVOID pAllocated = SplitPlaceholder(pReserved, reservedEnd, toAllocate); if (!pAllocated) - { - _ASSERTE(!"FAILED"); goto FAILED; - } pAllocated = CommitIntoPlaceholder(pAllocated, toAllocate, PAGE_READWRITE); if (!pAllocated) - { - _ASSERTE(!"FAILED"); goto FAILED; - } m_imageParts[imagePartIndex++] = AllocatedPart(pAllocated); @@ -1122,10 +1103,13 @@ void* FlatImageLayout::LoadImageByMappingParts(SIZE_T* m_imageParts) const } } - // Is there reserved space that we did not use? If so, release it. + // Is there reserved space that we did not use? if (pReserved < reservedEnd) { - VirtualFree(pReserved, 0, MEM_RELEASE); + // we can handle an image that request extra VM size, but it may indicate a noncompliant R2R PE. + _ASSERTE(!"Unused part of an image."); + // can't MEM_RELEASE the unused part yet, since the image must be contiguous. (see AssociateMemoryWithLoaderAllocator) + m_imageParts[imagePartIndex++] = AllocatedPart(pReserved); } return loadedHeader; From 3e2c617034f40f44106507de5525d3359ecf10d6 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Fri, 10 Dec 2021 11:02:32 -0800 Subject: [PATCH 07/14] comments --- src/coreclr/vm/peimage.h | 58 ++++++++++++++++++++++++++++------ src/coreclr/vm/peimagelayout.h | 47 ++++++++++++++------------- 2 files changed, 73 insertions(+), 32 deletions(-) diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index 3fbba2cba02539..e7600d5a12e436 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -28,17 +28,55 @@ class SimpleRWLock; class Crst; // -------------------------------------------------------------------------------- -// PEImage is a PE file loaded by our "simulated LoadLibrary" mechanism. A PEImage -// can be loaded either FLAT (same layout as on disk) or MAPPED (PE sections -// mapped into virtual addresses.) +// PEImage is a PE file loaded into memory. +// +// The actual data is represented by PEImageLayout instances which are created on demand. // -// The MAPPED format is currently limited to "IL only" images - this can be checked -// for via PEDecoder::IsILOnlyImage. +// Various PEImageLayouts can be classified into two kinds - +// - Flat - the same layout as on disk/array or +// +// - Loaded - PE sections are mapped into virtual addresses. +// PE relocations are applied +// native exception handlers are registered with OS. +// +// Flat layouts are sufficient for operatons that do not require running native code, +// Anything based on RVA, such as retrieving IL method bodies, is slightly less efficient, +// since RVA must be translated to file form. +// The additional cost is not very high though, since our PEs have only a few sections. +// +// Loaded layouts are functional supersets of Flat - anything that can be done with Flat +// can be done with Loaded. +// +// Running native code in the PE (i.e. R2R or IJW) scenarios require Loaded layout. +// In a case of IJW, the PE must be loaded by the native loader to ensure that native dependencies +// are resolved. +// It is possible to execute R2R assembly from Flat layout in IL mode, but its R2R functionality +// will be disabled. +// +// In some scenarios we create Loaded layouts by manually mapping images into memory. +// That is particularly true on Unix where we cannot rely on OS loader. +// Manual creation of layouts is limited to "IL only" images - this can be checked +// for via `PEDecoder::IsILOnlyImage` +// NOTE: historically, and somewhat confusingly, R2R PEs are considered IsILOnlyImage for this +// purpose. That is true even for composite R2R PEs that do not contain IL. +// +// A PEImage, depending on scenario, may end up creating both Flat and Loaded layouts, +// thus it has two slots - m_pLayouts[IMAGE_COUNT]. +// +// m_pLayouts[IMAGE_FLAT] +// When initialized contains a layout that allows operations for which Flat layout is sufficient - +// i.e. reading metadata +// +// m_pLayouts[IMAGE_LOADED] +// When initialized contains a layout that allows loading/running code. +// +// The layouts can only be unloaded together with the owning PEImage, so if we have Flat and +// then need Loaded, we can only add one more. Thus we have two slots. +// +// Often the slots refer to the same layout though. That is because if we create Loaded before Flat, +// we put it into both slots, since it is functionally a superset. +// Also the pure-IL assemblies do not require Loaded, so we may put Flat into both slots. // -// NOTE: PEImage will NEVER call LoadLibrary. -// -------------------------------------------------------------------------------- - - #define CV_SIGNATURE_RSDS 0x53445352 @@ -285,7 +323,7 @@ class PEImage final }; SimpleRWLock *m_pLayoutLock; - PTR_PEImageLayout m_pLayouts[IMAGE_COUNT] ; + PTR_PEImageLayout m_pLayouts[IMAGE_COUNT]; #ifdef METADATATRACKER_DATA class MetaDataTracker *m_pMDTracker; diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index 1100a2085118af..73039057cd8666 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -76,9 +76,28 @@ class PEImageLayout : public PEDecoder typedef ReleaseHolder PEImageLayoutHolder; -class FlatImageLayout; +// A simple layout where data stays the same as in the input (file or a byte array) +class FlatImageLayout : public PEImageLayout +{ + VPTR_VTABLE_CLASS(FlatImageLayout, PEImageLayout) + VPTR_UNIQUE(0x59) +protected: + CLRMapViewHolder m_FileView; +public: + HandleHolder m_FileMap; + +#ifndef DACCESS_COMPILE + FlatImageLayout(PEImage* pOwner); + FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T size); + void* LoadImageByCopyingParts(SIZE_T* m_imageParts) const; +#if TARGET_WINDOWS + void* LoadImageByMappingParts(SIZE_T* m_imageParts) const; +#endif +#endif +}; -// ConvertedImageView is for the case when we manually layout a flat image +// ConvertedImageView is for the case when we construct a loaded +// layout by mapping or copying portions of a flat layout class ConvertedImageLayout: public PEImageLayout { VPTR_VTABLE_CLASS(ConvertedImageLayout,PEImageLayout) @@ -94,6 +113,7 @@ class ConvertedImageLayout: public PEImageLayout SIZE_T m_imageParts[MAX_PARTS]; }; +// LoadedImageLayout is for the case when we construct a loaded layout directly class LoadedImageLayout: public PEImageLayout { VPTR_VTABLE_CLASS(LoadedImageLayout,PEImageLayout) @@ -113,27 +133,10 @@ class LoadedImageLayout: public PEImageLayout #endif // !DACCESS_COMPILE }; -class FlatImageLayout: public PEImageLayout -{ - VPTR_VTABLE_CLASS(FlatImageLayout,PEImageLayout) - VPTR_UNIQUE(0x59) -protected: - CLRMapViewHolder m_FileView; -public: - HandleHolder m_FileMap; - -#ifndef DACCESS_COMPILE - FlatImageLayout(PEImage* pOwner); - FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T size); - void* LoadImageByCopyingParts(SIZE_T* m_imageParts) const; -#if TARGET_WINDOWS - void* LoadImageByMappingParts(SIZE_T* m_imageParts) const; -#endif -#endif - -}; - #ifndef DACCESS_COMPILE +// A special layout that is used to load standalone composite r2r files. +// This layout is not owned by a PEImage and created by simply loading the file +// at the given path. class NativeImageLayout : public PEImageLayout { VPTR_VTABLE_CLASS(NativeImageLayout, PEImageLayout) From 23945fb2b716d1e2f4bec0223c82964c6061966c Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Fri, 10 Dec 2021 18:03:00 -0800 Subject: [PATCH 08/14] move EnsureLoaded into Assemby::Init --- src/coreclr/vm/assembly.cpp | 39 +++++++++++++++++++---------------- src/coreclr/vm/assembly.hpp | 1 - src/coreclr/vm/domainfile.cpp | 2 -- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index e6167062add89d..d7e9bcc4b2252e 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -189,11 +189,17 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat m_pClassLoader = new ClassLoader(this); m_pClassLoader->Init(pamTracker); - if (GetManifestFile()->IsDynamic()) + PEAssembly* pPEAssembly = GetManifestFile(); + + // Module::Create will initialize R2R support + // make sure the PE is loaded or R2R will be disabled. + pPEAssembly->EnsureLoaded(); + + if (pPEAssembly->IsDynamic()) // manifest modules of dynamic assemblies are always transient - m_pManifest = ReflectionModule::Create(this, GetManifestFile(), pamTracker, REFEMIT_MANIFEST_MODULE_NAME); + m_pManifest = ReflectionModule::Create(this, pPEAssembly, pamTracker, REFEMIT_MANIFEST_MODULE_NAME); else - m_pManifest = Module::Create(this, mdFileNil, GetManifestFile(), pamTracker); + m_pManifest = Module::Create(this, mdFileNil, pPEAssembly, pamTracker); FastInterlockIncrement((LONG*)&g_cAssemblies); @@ -208,30 +214,27 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat // loading it entirely. //CacheFriendAssemblyInfo(); - { - CANNOTTHROWCOMPLUSEXCEPTION(); - FAULT_FORBID(); - //Cannot fail after this point. - - PublishModuleIntoAssembly(m_pManifest); - - return; // Explicit return to let you know you are NOT welcome to add code after the CANNOTTHROW/FAULT_FORBID expires - } -} - -void Assembly::AssociateMemoryIfCollectible() -{ if (IsCollectible()) { COUNT_T size; - BYTE* start = (BYTE*)m_pManifest->GetPEAssembly()->GetLoadedImageContents(&size); + BYTE* start = (BYTE*)pPEAssembly->GetLoadedImageContents(&size); - // We should have the content loaded at this time. There will be no other attempt to start tracking. + // We should have the content loaded at this time. There will be no other attempt to associate memory. _ASSERTE(start != NULL); GCX_COOP(); LoaderAllocator::AssociateMemoryWithLoaderAllocator(start, start + size, m_pLoaderAllocator); } + + { + CANNOTTHROWCOMPLUSEXCEPTION(); + FAULT_FORBID(); + //Cannot fail after this point. + + PublishModuleIntoAssembly(m_pManifest); + + return; // Explicit return to let you know you are NOT welcome to add code after the CANNOTTHROW/FAULT_FORBID expires + } } Assembly::~Assembly() diff --git a/src/coreclr/vm/assembly.hpp b/src/coreclr/vm/assembly.hpp index 0d8830b6205dee..f8de5c162e7524 100644 --- a/src/coreclr/vm/assembly.hpp +++ b/src/coreclr/vm/assembly.hpp @@ -81,7 +81,6 @@ class Assembly public: Assembly(BaseDomain *pDomain, PEAssembly *pPEAssembly, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible); void Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator); - void AssociateMemoryIfCollectible(); void StartUnload(); void Terminate( BOOL signalProfiler = TRUE ); diff --git a/src/coreclr/vm/domainfile.cpp b/src/coreclr/vm/domainfile.cpp index b8f033637d784a..22796801543b31 100644 --- a/src/coreclr/vm/domainfile.cpp +++ b/src/coreclr/vm/domainfile.cpp @@ -525,8 +525,6 @@ void DomainFile::LoadLibrary() } CONTRACTL_END; - GetPEAssembly()->EnsureLoaded(); - ((DomainAssembly*)this)->GetAssembly()->AssociateMemoryIfCollectible(); } void DomainFile::PostLoadLibrary() From be5f1fedddd11b3db93acbd189cc0f3a6bac92f4 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:23:20 -0800 Subject: [PATCH 09/14] A fix for IsDynamic() case. --- src/coreclr/vm/assembly.cpp | 4 ++-- src/coreclr/vm/peimagelayout.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index d7e9bcc4b2252e..09c6ff22c2f838 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -191,7 +191,7 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat PEAssembly* pPEAssembly = GetManifestFile(); - // Module::Create will initialize R2R support + // "Module::Create" will initialize R2R support, if there is an R2R header. // make sure the PE is loaded or R2R will be disabled. pPEAssembly->EnsureLoaded(); @@ -214,7 +214,7 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat // loading it entirely. //CacheFriendAssemblyInfo(); - if (IsCollectible()) + if (IsCollectible() && !pPEAssembly->IsDynamic()) { COUNT_T size; BYTE* start = (BYTE*)pPEAssembly->GetLoadedImageContents(&size); diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index b7b9b27108e0ef..7f27a8cc52ced6 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -402,7 +402,6 @@ ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source) bool relocationMustWriteCopy = false; void* loadedImage = NULL; - // TODO: VS deal with LOG things. LOG((LF_LOADER, LL_INFO100, "PEImage: Opening manually mapped stream\n")); #ifdef TARGET_WINDOWS From 2bf609d7dd55973615fa0f9c626f7ac35b9e5b76 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Sun, 12 Dec 2021 19:00:16 -0800 Subject: [PATCH 10/14] fix for preferred base, if used. --- src/coreclr/vm/peimagelayout.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 7f27a8cc52ced6..262e4a3ce506e7 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -744,12 +744,15 @@ void* FlatImageLayout::LoadImageByCopyingParts(SIZE_T* m_imageParts) const #ifdef FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION if (g_useDefaultBaseAddr) { - preferredBase = (void*)source->GetPreferredBase(); + preferredBase = (void*)GetPreferredBase(); } #endif // FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION COUNT_T allocSize = ALIGN_UP(this->GetVirtualSize(), g_SystemInfo.dwAllocationGranularity); LPVOID base = ClrVirtualAlloc(preferredBase, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (base == NULL && preferredBase != NULL) + base = ClrVirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (base == NULL) ThrowLastError(); From 1e0a9335af71df254a67ab8bfd0ede7b35f77761 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Sun, 12 Dec 2021 19:00:58 -0800 Subject: [PATCH 11/14] disable failing scenario --- src/tests/readytorun/crossgen2/Program.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tests/readytorun/crossgen2/Program.cs b/src/tests/readytorun/crossgen2/Program.cs index b0c011e5b94a83..49213047ec74aa 100644 --- a/src/tests/readytorun/crossgen2/Program.cs +++ b/src/tests/readytorun/crossgen2/Program.cs @@ -2228,7 +2228,15 @@ public static int Main(string[] args) RunTest("ExplicitlySizedClassTest", ExplicitlySizedClassTest()); RunTest("GenericLdtokenTest", GenericLdtokenTest()); RunTest("ArrayLdtokenTests", ArrayLdtokenTests()); - RunTest("TestGenericMDArrayBehavior", TestGenericMDArrayBehavior()); + + // TODO: BUG BUG: allocation of MD arrays is failing on OSX/ARM64 + // the corresponding vararg helper does run as expected + // see HCIMPL2VA(Object*, JIT_NewMDArr, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs) + if (!(RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && (RuntimeInformation.ProcessArchitecture == Architecture.Arm64))) + { + RunTest("TestGenericMDArrayBehavior", TestGenericMDArrayBehavior()); + } + RunTest("TestWithStructureNonBlittableFieldDueToGenerics", TestWithStructureNonBlittableFieldDueToGenerics()); RunTest("TestSingleElementStructABI", TestSingleElementStructABI()); RunTest("TestEnumLayoutAlignments", TestEnumLayoutAlignments()); From 5cc040ec30b3396514f963edcd9237d424afb2c8 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Mon, 13 Dec 2021 15:38:17 -0800 Subject: [PATCH 12/14] PR feedback --- .../ObjectWriter/R2RPEBuilder.cs | 14 +++-- src/coreclr/utilcode/pedecoder.cpp | 5 +- src/coreclr/vm/peimage.cpp | 52 +++++++------------ src/coreclr/vm/peimage.h | 19 +++---- 4 files changed, 43 insertions(+), 47 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs index 55d1054cbb6b4a..608e159f7f6e36 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs @@ -683,17 +683,21 @@ public static PEHeaderBuilder Create(Subsystem subsystem, TargetDetails target) ulong imageBase = is64BitTarget ? PE64HeaderConstants.DllImageBase : PE32HeaderConstants.ImageBase; int fileAlignment = 0x200; - if (target.IsWindows || !is64BitTarget) + bool isWindowsOr32bit = target.IsWindows || !is64BitTarget; + if (isWindowsOr32bit) { - // To minimize wasted VA space on 32-bit systems, align file to page boundaries (presumed to be 4K) + // To minimize wasted VA space on 32-bit systems (regardless of OS), + // align file to page boundaries (presumed to be 4K) // - // On Windows we use 4K file alignment, per requirements of memory mapping API (MapViewOfFile3, et al). - // We also want files always accepted by the native loader, thus we can't use the same approach as on Unix. + // On Windows we use 4K file alignment (regardless of ptr size), + // per requirements of memory mapping API (MapViewOfFile3, et al). + // The alternative could be using the same approach as on Unix, but that would result in PEs + // incompatible with OS loader. While that is not a problem on Unix, we do not want that on Windows. fileAlignment = 0x1000; } int sectionAlignment = 0x1000; - if (!target.IsWindows && is64BitTarget) + if (!isWindowsOr32bit) { // On 64bit Linux, we must match the bottom 12 bits of section RVA's to their file offsets. For this reason // we need the same alignment for both. diff --git a/src/coreclr/utilcode/pedecoder.cpp b/src/coreclr/utilcode/pedecoder.cpp index b5f5def348f265..671233cc411178 100644 --- a/src/coreclr/utilcode/pedecoder.cpp +++ b/src/coreclr/utilcode/pedecoder.cpp @@ -373,7 +373,10 @@ CHECK PEDecoder::CheckSection(COUNT_T previousAddressEnd, COUNT_T addressStart, // Check expected alignments #if TARGET_WINDOWS - // R2R files on Unix do not align section RVAs + // On Windows we expect section starts to be a multiple of SectionAlignment. + // That is not an explicit requirement in the documentation, but it looks like OS loader expects it. + // In contrast to this, in Unix R2R files we keep lower 16bits of sections RVA and + // this condition does not hold. CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment))); #endif CHECK(CheckAligned(offsetStart, VAL32(pNT->OptionalHeader.FileAlignment))); diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index a40099e78f3d8d..4c517a1bd3e5f4 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -965,61 +965,49 @@ HANDLE PEImage::GetFileHandle() if (m_hFile!=INVALID_HANDLE_VALUE) return m_hFile; - { - ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS); - m_hFile=WszCreateFile((LPCWSTR) GetPathToLoad(), - GENERIC_READ -#if TARGET_WINDOWS - // the file may have native code sections, make sure we are allowed to execute the file - | GENERIC_EXECUTE -#endif - , - FILE_SHARE_READ|FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - } + HRESULT hr = TryOpenFile(/*takeLock*/ false); if (m_hFile == INVALID_HANDLE_VALUE) { #if !defined(DACCESS_COMPILE) - EEFileLoadException::Throw(GetPathToLoad(), HRESULT_FROM_WIN32(GetLastError())); + EEFileLoadException::Throw(GetPathToLoad(), hr); #else // defined(DACCESS_COMPILE) - ThrowLastError(); + ThrowHR(hr); #endif // !defined(DACCESS_COMPILE) } return m_hFile; } -HRESULT PEImage::TryOpenFile() +HRESULT PEImage::TryOpenFile(bool takeLock) { STANDARD_VM_CONTRACT; - SimpleWriteLockHolder lock(m_pLayoutLock); + SimpleWriteLockHolder lock(m_pLayoutLock, takeLock); if (m_hFile!=INVALID_HANDLE_VALUE) return S_OK; - { - ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); - m_hFile=WszCreateFile((LPCWSTR)GetPathToLoad(), - GENERIC_READ + + ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + m_hFile=WszCreateFile((LPCWSTR)GetPathToLoad(), + GENERIC_READ #if TARGET_WINDOWS - // the file may have native code sections, make sure we are allowed to execute the file - | GENERIC_EXECUTE + // the file may have native code sections, make sure we are allowed to execute the file + | GENERIC_EXECUTE #endif - , - FILE_SHARE_READ|FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - } + , + FILE_SHARE_READ|FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (m_hFile != INVALID_HANDLE_VALUE) return S_OK; + if (GetLastError()) return HRESULT_FROM_WIN32(GetLastError()); + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index e7600d5a12e436..7b481569606efc 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -36,10 +36,10 @@ class Crst; // - Flat - the same layout as on disk/array or // // - Loaded - PE sections are mapped into virtual addresses. -// PE relocations are applied -// native exception handlers are registered with OS. +// PE relocations are applied. +// Native exception handlers are registered with OS (on Windows). // -// Flat layouts are sufficient for operatons that do not require running native code, +// Flat layouts are sufficient for operations that do not require running native code, // Anything based on RVA, such as retrieving IL method bodies, is slightly less efficient, // since RVA must be translated to file form. // The additional cost is not very high though, since our PEs have only a few sections. @@ -47,15 +47,16 @@ class Crst; // Loaded layouts are functional supersets of Flat - anything that can be done with Flat // can be done with Loaded. // -// Running native code in the PE (i.e. R2R or IJW) scenarios require Loaded layout. +// Running native code in the PE (i.e. R2R or IJW scenarios) requires Loaded layout. +// It is possible to execute R2R assembly from Flat layout in IL mode, but its R2R functionality +// will be disabled. When R2R is explicitly turned off, Flat is sufficient for any scenario with +// R2R assemblies. // In a case of IJW, the PE must be loaded by the native loader to ensure that native dependencies // are resolved. -// It is possible to execute R2R assembly from Flat layout in IL mode, but its R2R functionality -// will be disabled. // // In some scenarios we create Loaded layouts by manually mapping images into memory. // That is particularly true on Unix where we cannot rely on OS loader. -// Manual creation of layouts is limited to "IL only" images - this can be checked +// Manual creation of layouts is limited to "IL only" images. This can be checked // for via `PEDecoder::IsILOnlyImage` // NOTE: historically, and somewhat confusingly, R2R PEs are considered IsILOnlyImage for this // purpose. That is true even for composite R2R PEs that do not contain IL. @@ -139,12 +140,12 @@ class PEImage final BOOL IsFile(); BOOL IsInBundle() const; - HANDLE GetFileHandle(); INT64 GetOffset() const; INT64 GetSize() const; INT64 GetUncompressedSize() const; - HRESULT TryOpenFile(); + HANDLE GetFileHandle(); + HRESULT TryOpenFile(bool takeLock = false); void GetMVID(GUID *pMvid); BOOL HasV1Metadata(); From 30be65f4eeeaaf2a041d2cb00ab2fc9086843309 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Mon, 13 Dec 2021 16:55:52 -0800 Subject: [PATCH 13/14] Typo (CENTINEL --> SENTINEL) --- src/coreclr/vm/peimagelayout.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 262e4a3ce506e7..ae2d2ca5f4bfc3 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -840,8 +840,8 @@ MapViewOfFile3Fn pMapViewOfFile3 = NULL; static bool HavePlaceholderAPI() { - const MapViewOfFile3Fn INVALID_ADDRESS_CENTINEL = (MapViewOfFile3Fn)1; - if (pMapViewOfFile3 == INVALID_ADDRESS_CENTINEL) + const MapViewOfFile3Fn INVALID_ADDRESS_SENTINEL = (MapViewOfFile3Fn)1; + if (pMapViewOfFile3 == INVALID_ADDRESS_SENTINEL) { return false; } @@ -859,7 +859,7 @@ static bool HavePlaceholderAPI() if (pMapViewOfFile3 == NULL || pVirtualAlloc2 == NULL) { - pMapViewOfFile3 = INVALID_ADDRESS_CENTINEL; + pMapViewOfFile3 = INVALID_ADDRESS_SENTINEL; return false; } } From 473f7344994e71eb0b1ce2aa3ed8e379e0ec2fc6 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Mon, 13 Dec 2021 18:54:58 -0800 Subject: [PATCH 14/14] added a bug link to a disabled test scenario + couple comment tweaks --- src/coreclr/vm/peimage.h | 6 +++--- src/tests/readytorun/crossgen2/Program.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index 7b481569606efc..6451283d007ce4 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -41,7 +41,7 @@ class Crst; // // Flat layouts are sufficient for operations that do not require running native code, // Anything based on RVA, such as retrieving IL method bodies, is slightly less efficient, -// since RVA must be translated to file form. +// since RVA must be translated to file offsets by iterating through section headers. // The additional cost is not very high though, since our PEs have only a few sections. // // Loaded layouts are functional supersets of Flat - anything that can be done with Flat @@ -75,8 +75,8 @@ class Crst; // then need Loaded, we can only add one more. Thus we have two slots. // // Often the slots refer to the same layout though. That is because if we create Loaded before Flat, -// we put it into both slots, since it is functionally a superset. -// Also the pure-IL assemblies do not require Loaded, so we may put Flat into both slots. +// we put Loaded into both slots, since it is functionally a superset of Flat. +// Also for pure-IL assemblies Flat is sufficient for anything, so we may put Flat into both slots. // #define CV_SIGNATURE_RSDS 0x53445352 diff --git a/src/tests/readytorun/crossgen2/Program.cs b/src/tests/readytorun/crossgen2/Program.cs index 49213047ec74aa..8733594655555a 100644 --- a/src/tests/readytorun/crossgen2/Program.cs +++ b/src/tests/readytorun/crossgen2/Program.cs @@ -2229,9 +2229,9 @@ public static int Main(string[] args) RunTest("GenericLdtokenTest", GenericLdtokenTest()); RunTest("ArrayLdtokenTests", ArrayLdtokenTests()); - // TODO: BUG BUG: allocation of MD arrays is failing on OSX/ARM64 - // the corresponding vararg helper does run as expected - // see HCIMPL2VA(Object*, JIT_NewMDArr, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs) + // TODO: BUG BUG: allocation of MD arrays fails on OSX/ARM64 (https://github.com/dotnet/runtime/issues/62747) + // the vararg helper does not run as expected on OSX/ARM64 due to ABI mismatch - + // HCIMPL2VA(Object*, JIT_NewMDArr, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs) if (!(RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && (RuntimeInformation.ProcessArchitecture == Architecture.Arm64))) { RunTest("TestGenericMDArrayBehavior", TestGenericMDArrayBehavior());