Skip to content

Commit 35e4e97

Browse files
authored
PE loader/image cleanups. No-copy mapping of R2R PEs on Windows. (#61938)
* removed IMAGE_MAPPED * removed RawImageLayout * delete MappedImageLayout * use mem-mapping on Windows * tweaks and touchups * a few cleanups * comments * move EnsureLoaded into Assemby::Init * A fix for IsDynamic() case. * fix for preferred base, if used. * disable failing scenario * PR feedback * Typo (CENTINEL --> SENTINEL) * added a bug link to a disabled test scenario + couple comment tweaks
1 parent 842f4d7 commit 35e4e97

23 files changed

Lines changed: 911 additions & 831 deletions

File tree

src/coreclr/debug/daccess/request.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,11 +1344,7 @@ ClrDataAccess::GetMethodDescName(CLRDATA_ADDRESS methodDesc, unsigned int count,
13441344
if (pMD->IsLCGMethod() || pMD->IsILStub())
13451345
{
13461346
// In heap dumps, trying to format the signature can fail
1347-
// in certain cases because StoredSigMethodDesc::m_pSig points
1348-
// to the IMAGE_MAPPED layout (in the PEImage::m_pLayouts array).
1349-
// We save only the IMAGE_LOADED layout to the heap dump. Rather
1350-
// than bloat the dump, we just drop the signature in these
1351-
// cases.
1347+
// in certain cases.
13521348

13531349
str.Clear();
13541350
TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst);

src/coreclr/inc/pedecoder.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,6 @@ class PEDecoder
237237
BOOL IsILOnly() const;
238238
CHECK CheckILOnly() const;
239239

240-
void LayoutILOnly(void *base, bool enableExecution) const;
241-
242240
// Strong name & hashing support
243241

244242
BOOL HasStrongNameSignature() const;
@@ -348,6 +346,7 @@ class PEDecoder
348346
IMAGE_SECTION_HEADER *OffsetToSection(COUNT_T fileOffset) const;
349347

350348
void SetRelocated();
349+
IMAGE_NT_HEADERS* FindNTHeaders() const;
351350

352351
private:
353352

@@ -364,7 +363,6 @@ class PEDecoder
364363

365364
static PTR_IMAGE_SECTION_HEADER FindFirstSection(IMAGE_NT_HEADERS * pNTHeaders);
366365

367-
IMAGE_NT_HEADERS *FindNTHeaders() const;
368366
IMAGE_COR20_HEADER *FindCorHeader() const;
369367
READYTORUN_HEADER *FindReadyToRunHeader() const;
370368

src/coreclr/inc/pedecoder.inl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ inline PEDecoder::PEDecoder(PTR_VOID mappedBase, bool fixedUp /*= FALSE*/)
100100
{
101101
CONSTRUCTOR_CHECK;
102102
PRECONDITION(CheckPointer(mappedBase));
103-
PRECONDITION(CheckAligned(mappedBase, GetOsPageSize()));
104103
PRECONDITION(PEDecoder(mappedBase,fixedUp).CheckNTHeaders());
105104
THROWS;
106105
GC_NOTRIGGER;
@@ -172,7 +171,6 @@ inline HRESULT PEDecoder::Init(void *mappedBase, bool fixedUp /*= FALSE*/)
172171
NOTHROW;
173172
GC_NOTRIGGER;
174173
PRECONDITION(CheckPointer(mappedBase));
175-
PRECONDITION(CheckAligned(mappedBase, GetOsPageSize()));
176174
PRECONDITION(!HasContents());
177175
}
178176
CONTRACTL_END;

src/coreclr/inc/vptr_list.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,12 @@ VPTR_CLASS(TailCallStubManager)
4040
#endif
4141
VPTR_CLASS(CallCountingStubManager)
4242
VPTR_CLASS(PEAssembly)
43+
4344
VPTR_CLASS(PEImageLayout)
44-
VPTR_CLASS(RawImageLayout)
4545
VPTR_CLASS(ConvertedImageLayout)
46-
VPTR_CLASS(MappedImageLayout)
47-
#if !defined(TARGET_UNIX)
4846
VPTR_CLASS(LoadedImageLayout)
49-
#endif // !TARGET_UNIX
5047
VPTR_CLASS(FlatImageLayout)
48+
5149
#ifdef FEATURE_COMINTEROP
5250
VPTR_CLASS(ComMethodFrame)
5351
VPTR_CLASS(ComPlusMethodFrame)

src/coreclr/pal/src/map/map.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2043,9 +2043,9 @@ MAPmmapAndRecord(
20432043

20442044
// Set the requested mapping with forced PROT_WRITE to ensure data from the file can be read there,
20452045
// read the data in and finally remove the forced PROT_WRITE
2046-
if ((mprotect(pvBaseAddress, len + adjust, prot | PROT_WRITE) == -1) ||
2046+
if ((mprotect(pvBaseAddress, len + adjust, PROT_WRITE) == -1) ||
20472047
(pread(fd, pvBaseAddress, len + adjust, offset - adjust) == -1) ||
2048-
(((prot & PROT_WRITE) == 0) && mprotect(pvBaseAddress, len + adjust, prot) == -1))
2048+
(mprotect(pvBaseAddress, len + adjust, prot) == -1))
20492049
{
20502050
palError = FILEGetLastErrorFromErrno();
20512051
}

src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -683,17 +683,29 @@ public static PEHeaderBuilder Create(Subsystem subsystem, TargetDetails target)
683683
ulong imageBase = is64BitTarget ? PE64HeaderConstants.DllImageBase : PE32HeaderConstants.ImageBase;
684684

685685
int fileAlignment = 0x200;
686-
if (!target.IsWindows && !is64BitTarget)
686+
bool isWindowsOr32bit = target.IsWindows || !is64BitTarget;
687+
if (isWindowsOr32bit)
687688
{
688-
// To minimize wasted VA space on 32-bit systems, align file to page boundaries (presumed to be 4K)
689+
// To minimize wasted VA space on 32-bit systems (regardless of OS),
690+
// align file to page boundaries (presumed to be 4K)
691+
//
692+
// On Windows we use 4K file alignment (regardless of ptr size),
693+
// per requirements of memory mapping API (MapViewOfFile3, et al).
694+
// The alternative could be using the same approach as on Unix, but that would result in PEs
695+
// incompatible with OS loader. While that is not a problem on Unix, we do not want that on Windows.
689696
fileAlignment = 0x1000;
690697
}
691698

692699
int sectionAlignment = 0x1000;
693-
if (!target.IsWindows && is64BitTarget)
700+
if (!isWindowsOr32bit)
694701
{
695-
// On Linux, we must match the bottom 12 bits of section RVA's to their file offsets. For this reason
702+
// On 64bit Linux, we must match the bottom 12 bits of section RVA's to their file offsets. For this reason
696703
// we need the same alignment for both.
704+
//
705+
// In addition to that we specify section RVAs to be at least 64K apart, which is > page on most systems.
706+
// It ensures that the sections will not overlap when mapped from a singlefile bundle, which introduces a sub-page skew.
707+
//
708+
// Such format would not be accepted by OS loader on Windows, but it is not a problem on Unix.
697709
sectionAlignment = fileAlignment;
698710
}
699711

src/coreclr/utilcode/pedecoder.cpp

Lines changed: 6 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,13 @@ CHECK PEDecoder::CheckSection(COUNT_T previousAddressEnd, COUNT_T addressStart,
372372
CHECK(alignedSize >= VAL32(pNT->OptionalHeader.SizeOfImage));
373373

374374
// Check expected alignments
375+
#if TARGET_WINDOWS
376+
// On Windows we expect section starts to be a multiple of SectionAlignment.
377+
// That is not an explicit requirement in the documentation, but it looks like OS loader expects it.
378+
// In contrast to this, in Unix R2R files we keep lower 16bits of sections RVA and
379+
// this condition does not hold.
375380
CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment)));
381+
#endif
376382
CHECK(CheckAligned(offsetStart, VAL32(pNT->OptionalHeader.FileAlignment)));
377383
CHECK(CheckAligned(offsetSize, VAL32(pNT->OptionalHeader.FileAlignment)));
378384

@@ -1659,86 +1665,6 @@ CHECK PEDecoder::CheckILOnlyEntryPoint() const
16591665
}
16601666
#endif // TARGET_X86
16611667

1662-
#ifndef DACCESS_COMPILE
1663-
1664-
void PEDecoder::LayoutILOnly(void *base, bool enableExecution) const
1665-
{
1666-
CONTRACT_VOID
1667-
{
1668-
INSTANCE_CHECK;
1669-
PRECONDITION(CheckZeroedMemory(base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfImage)));
1670-
// Ideally we would require the layout address to honor the section alignment constraints.
1671-
// However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this
1672-
// case, we can only guarantee OS page alignment (which after all, is good enough.)
1673-
PRECONDITION(CheckAligned((SIZE_T)base, GetOsPageSize()));
1674-
THROWS;
1675-
GC_NOTRIGGER;
1676-
}
1677-
CONTRACT_END;
1678-
1679-
// We're going to copy everything first, and write protect what we need to later.
1680-
1681-
// First, copy headers
1682-
CopyMemory(base, (void *)m_base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders));
1683-
1684-
// Now, copy all sections to appropriate virtual address
1685-
1686-
IMAGE_SECTION_HEADER *sectionStart = IMAGE_FIRST_SECTION(FindNTHeaders());
1687-
IMAGE_SECTION_HEADER *sectionEnd = sectionStart + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
1688-
1689-
IMAGE_SECTION_HEADER *section = sectionStart;
1690-
while (section < sectionEnd)
1691-
{
1692-
// Raw data may be less than section size if tail is zero, but may be more since VirtualSize is
1693-
// not padded.
1694-
DWORD size = min(VAL32(section->SizeOfRawData), VAL32(section->Misc.VirtualSize));
1695-
1696-
CopyMemory((BYTE *) base + VAL32(section->VirtualAddress), (BYTE *) m_base + VAL32(section->PointerToRawData), size);
1697-
1698-
// Note that our memory is zeroed already, so no need to initialize any tail.
1699-
1700-
section++;
1701-
}
1702-
1703-
// Apply write protection to copied headers
1704-
DWORD oldProtection;
1705-
if (!ClrVirtualProtect((void *) base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders),
1706-
PAGE_READONLY, &oldProtection))
1707-
ThrowLastError();
1708-
1709-
// Finally, apply proper protection to copied sections
1710-
for (section = sectionStart; section < sectionEnd; section++)
1711-
{
1712-
// Add appropriate page protection.
1713-
DWORD newProtection;
1714-
if (!enableExecution)
1715-
{
1716-
if (section->Characteristics & IMAGE_SCN_MEM_WRITE)
1717-
continue;
1718-
1719-
newProtection = PAGE_READONLY;
1720-
}
1721-
else
1722-
{
1723-
newProtection = section->Characteristics & IMAGE_SCN_MEM_EXECUTE ?
1724-
PAGE_EXECUTE_READ :
1725-
section->Characteristics & IMAGE_SCN_MEM_WRITE ?
1726-
PAGE_READWRITE :
1727-
PAGE_READONLY;
1728-
}
1729-
1730-
if (!ClrVirtualProtect((void*)((BYTE*)base + VAL32(section->VirtualAddress)),
1731-
VAL32(section->Misc.VirtualSize),
1732-
newProtection, &oldProtection))
1733-
{
1734-
ThrowLastError();
1735-
}
1736-
}
1737-
1738-
RETURN;
1739-
}
1740-
1741-
#endif // #ifndef DACCESS_COMPILE
17421668

17431669
bool ReadResourceDirectoryHeader(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rva, IMAGE_RESOURCE_DIRECTORY_ENTRY** ppDirectoryEntries, IMAGE_RESOURCE_DIRECTORY **ppResourceDirectory)
17441670
{

src/coreclr/vm/assembly.cpp

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,17 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat
189189
m_pClassLoader = new ClassLoader(this);
190190
m_pClassLoader->Init(pamTracker);
191191

192-
if (GetManifestFile()->IsDynamic())
192+
PEAssembly* pPEAssembly = GetManifestFile();
193+
194+
// "Module::Create" will initialize R2R support, if there is an R2R header.
195+
// make sure the PE is loaded or R2R will be disabled.
196+
pPEAssembly->EnsureLoaded();
197+
198+
if (pPEAssembly->IsDynamic())
193199
// manifest modules of dynamic assemblies are always transient
194-
m_pManifest = ReflectionModule::Create(this, GetManifestFile(), pamTracker, REFEMIT_MANIFEST_MODULE_NAME);
200+
m_pManifest = ReflectionModule::Create(this, pPEAssembly, pamTracker, REFEMIT_MANIFEST_MODULE_NAME);
195201
else
196-
m_pManifest = Module::Create(this, mdFileNil, GetManifestFile(), pamTracker);
202+
m_pManifest = Module::Create(this, mdFileNil, pPEAssembly, pamTracker);
197203

198204
FastInterlockIncrement((LONG*)&g_cAssemblies);
199205

@@ -208,15 +214,16 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat
208214
// loading it entirely.
209215
//CacheFriendAssemblyInfo();
210216

211-
if (IsCollectible())
217+
if (IsCollectible() && !pPEAssembly->IsDynamic())
212218
{
213219
COUNT_T size;
214-
BYTE *start = (BYTE*)m_pManifest->GetPEAssembly()->GetLoadedImageContents(&size);
215-
if (start != NULL)
216-
{
217-
GCX_COOP();
218-
LoaderAllocator::AssociateMemoryWithLoaderAllocator(start, start + size, m_pLoaderAllocator);
219-
}
220+
BYTE* start = (BYTE*)pPEAssembly->GetLoadedImageContents(&size);
221+
222+
// We should have the content loaded at this time. There will be no other attempt to associate memory.
223+
_ASSERTE(start != NULL);
224+
225+
GCX_COOP();
226+
LoaderAllocator::AssociateMemoryWithLoaderAllocator(start, start + size, m_pLoaderAllocator);
220227
}
221228

222229
{

src/coreclr/vm/assemblyname.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,9 @@ FCIMPL1(Object*, AssemblyNameNative::GetFileInformation, StringObject* filenameU
5252
SString sFileName(gc.filename->GetBuffer());
5353
PEImageHolder pImage = PEImage::OpenImage(sFileName, MDInternalImport_NoCache);
5454

55-
// Load the temporary image using a flat layout, instead of
56-
// waiting for it to happen during HasNTHeaders. This allows us to
57-
// get the assembly name for images that contain native code for a
58-
// non-native platform.
55+
// Ask for FLAT. We will only look at metadata and release the image shortly.
56+
// Besides we may be getting the assembly name for images that contain native code for a
57+
// non-native platform and would end up using flat anyways.
5958
PEImageLayout* pLayout = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_FLAT);
6059

6160
pImage->VerifyIsAssembly();

src/coreclr/vm/assemblynative.cpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,8 @@ Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pIma
138138
CONTRACT_END;
139139

140140
Assembly *pLoadedAssembly = NULL;
141-
142141
ReleaseHolder<BINDER_SPACE::Assembly> pAssembly;
143142

144-
// Force the image to be loaded and mapped so that subsequent loads do not
145-
// map a duplicate copy.
146-
if (pImage->IsFile())
147-
{
148-
pImage->Load();
149-
}
150-
else
151-
{
152-
pImage->LoadNoFile();
153-
}
154-
155143
DWORD dwMessageID = IDS_EE_FILELOAD_ERROR_GENERIC;
156144

157145
// Set the caller's assembly to be CoreLib
@@ -251,11 +239,7 @@ extern "C" void QCALLTYPE AssemblyNative_LoadFromStream(INT_PTR ptrNativeAssembl
251239
_ASSERTE((ptrAssemblyArray != NULL) && (cbAssemblyArrayLength > 0));
252240
_ASSERTE((ptrSymbolArray == NULL) || (cbSymbolArrayLength > 0));
253241

254-
// We must have a flat image stashed away since we need a private
255-
// copy of the data which we can verify before doing the mapping.
256-
PVOID pAssemblyArray = reinterpret_cast<PVOID>(ptrAssemblyArray);
257-
258-
PEImageHolder pILImage(PEImage::LoadFlat(pAssemblyArray, (COUNT_T)cbAssemblyArrayLength));
242+
PEImageHolder pILImage(PEImage::CreateFromByteArray((BYTE*)ptrAssemblyArray, (COUNT_T)cbAssemblyArrayLength));
259243

260244
// Need to verify that this is a valid CLR assembly.
261245
if (!pILImage->CheckILFormat())
@@ -318,7 +302,7 @@ extern "C" void QCALLTYPE AssemblyNative_LoadFromInMemoryModule(INT_PTR ptrNativ
318302
_ASSERTE(ptrNativeAssemblyBinder != NULL);
319303
_ASSERTE(hModule != NULL);
320304

321-
PEImageHolder pILImage(PEImage::LoadImage((HMODULE)hModule));
305+
PEImageHolder pILImage(PEImage::CreateFromHMODULE((HMODULE)hModule));
322306

323307
// Need to verify that this is a valid CLR assembly.
324308
if (!pILImage->HasCorHeader())

0 commit comments

Comments
 (0)