{
public partial class AssemblyLoadContext
{
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern void FlushPECaches();
+
[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern IntPtr InitializeAssemblyLoadContext(IntPtr ptrAssemblyLoadContext, bool fRepresentsTPALoadContext, bool isCollectible);
PALAPI
PAL_LOADMarkSectionAsNotNeeded(void * ptr);
+/*++
+ PAL_LOADMarkDontNeedCleanPages
+ Try to mark clean pages from range as not needed.
+Parameters:
+ IN base - base address of pe image
+ IN ptr - start address of range
+ IN size - size of range in bytes
+Return value:
+ TRUE - success
+ FALSE - failure
+--*/
+PALIMPORT
+BOOL
+PALAPI
+PAL_LOADMarkDontNeedCleanPages(PVOID base, PVOID ptr, SIZE_T size);
+
#ifdef UNICODE
#define LoadLibrary LoadLibraryW
#define LoadLibraryEx LoadLibraryExW
returns TRUE if successful, FALSE otherwise
--*/
BOOL MAPMarkSectionAsNotNeeded(LPCVOID lpAddress);
+
+ /*++
+ MAPMarkDontNeedCleanPages - try to mark clean pages from range as not needed
+ Parameters:
+ IN lpBase - base address of pe image
+ IN lpAddress - start address of range
+ IN size - size of range in bytes
+ Return value:
+ TRUE - success
+ FALSE - failure
+ --*/
+ BOOL MAPMarkDontNeedCleanPages(LPCVOID lpBase, LPCVOID lpAddress, SIZE_T size);
}
namespace CorUnix
BOOL PAL_LOADUnloadPEFile(void * ptr);
/*++
+ PAL_LOADMarkDontNeedCleanPages
+ Try to mark clean pages from range as not needed.
+Parameters:
+ IN base - base address of pe image
+ IN ptr - start address of range
+ IN size - size of range in bytes
+Return value:
+ TRUE - success
+ FALSE - failure
+--*/
+BOOL PAL_LOADMarkDontNeedCleanPages(PVOID base, PVOID ptr, SIZE_T size);
+
+/*++
Function:
PAL_LOADPreloadPEFile
}
/*++
+ PAL_LOADMarkDontNeedCleanPages
+ Try to mark clean pages from range as not needed.
+Parameters:
+ IN base - base address of pe image
+ IN ptr - start address of range
+ IN size - size of range in bytes
+Return value:
+ TRUE - success
+ FALSE - failure
+--*/
+BOOL
+PALAPI
+PAL_LOADMarkDontNeedCleanPages(PVOID base, PVOID ptr, SIZE_T size)
+{
+ BOOL retval = FALSE;
+ ENTRY("PAL_LOADMarkDontNeedCleanPages (base=%p, ptr=%p, size=%llu)\n", base, ptr, (long long unsigned) size);
+
+ if (nullptr == base)
+ {
+ ERROR( "Invalid pointer value\n" );
+ }
+ else
+ {
+ retval = MAPMarkDontNeedCleanPages(base, ptr, size);
+ }
+
+ LOGEXIT("PAL_LOADMarkDontNeedCleanPages returns %d\n", retval);
+ return retval;
+}
+
+/*++
PAL_GetSymbolModuleBase
Get base address of the module containing a given symbol
TRACE_(LOADER)("MAPMarkSectionAsNotNeeded returning %d\n", retval);
return retval;
}
+
+/*++
+ MAPMarkDontNeedCleanPages does similar thing as MAPMarkSectionAsNotNeeded, but marks specific read-only memory range instead of marking whole section
+Parameters:
+ IN lpBase - base address of pe image
+ IN lpAddress - start address of range
+ IN size - size of range in bytes
+Return value:
+ TRUE - success
+ FALSE - failure
+--*/
+BOOL MAPMarkDontNeedCleanPages(LPCVOID lpBase, LPCVOID lpAddress, SIZE_T size)
+{
+ TRACE_(LOADER)("MAPMarkDontNeedCleanPages(lpBase=%p, lpAddress=%p, size=%llu)\n", lpBase, lpAddress, (long long unsigned) size);
+
+ if ( NULL == lpBase )
+ {
+ ERROR_(LOADER)( "lpBase cannot be NULL\n" );
+ return FALSE;
+ }
+
+ BOOL retval = TRUE;
+ CPalThread * pThread = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+ PLIST_ENTRY pLink, pLinkNext;
+
+ // MappedViewList contains records for all mmaped dlls (both il-only and r2r), 1 record for il-only, multiple records for r2r
+ for(pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLinkNext)
+ {
+ pLinkNext = pLink->Flink;
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ // Skip views that do not correspond to lpBase dll
+ // pView->lpPEBaseAddress is set to 0, and pView->lpAddress is set to starting address of vma for il-only dll
+ // pView->lpPEBaseAddress is set to starting address of vma for R2R dll
+ if (!(pView->lpPEBaseAddress == lpBase || pView->lpPEBaseAddress == 0 && pView->lpAddress == lpBase))
+ {
+ continue;
+ }
+ // madvise for read only pages of PE files
+ if (pView->dwDesiredAccess != FILE_MAP_READ)
+ {
+ continue;
+ }
+
+ BYTE *pViewStart = (BYTE *)pView->lpAddress;
+ BYTE *pViewEnd = pViewStart + pView->NumberOfBytesToMap;
+ BYTE *pRangeStart = (lpAddress == NULL) ? pViewStart : (BYTE *)lpAddress;
+ BYTE *pRangeEnd = (size == 0) ? pViewEnd : pRangeStart + size;
+
+ if (pRangeEnd <= pRangeStart)
+ {
+ continue;
+ }
+ if (pViewEnd <= pRangeStart || pRangeEnd <= pViewStart)
+ {
+ continue;
+ }
+
+ BYTE *pStart = (pRangeStart <= pViewStart) ? pViewStart : pRangeStart;
+ BYTE *pEnd = (pRangeEnd <= pViewEnd) ? pRangeEnd : pViewEnd;
+ LPVOID lpStart = (LPVOID) pStart;
+ SIZE_T curSize = (SIZE_T)(pEnd - pStart);
+
+ if (lpStart != nullptr && curSize > 0)
+ {
+ if (-1 == madvise(lpStart, curSize, MADV_DONTNEED))
+ {
+ ERROR_(LOADER)("Unable to mark the section as NotNeeded.\n");
+ retval = FALSE;
+ break;
+ }
+ }
+ }
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ TRACE_(LOADER)("MAPMarkDontNeedCleanPages returning %d\n", retval);
+ return retval;
+}
\ No newline at end of file
return result;
}
+
+FCIMPL0(void, AssemblyNative::FlushPECaches)
+{
+ FCALL_CONTRACT;
+
+ PEImage::CleanAllReadOnlyPages();
+}
+FCIMPLEND
void QCALLTYPE GetExecutingAssembly(QCall::StackCrawlMarkHandle stackMark, QCall::ObjectHandleOnStack retAssembly);
static FCDECL0(FC_BOOL_RET, IsTracingEnabled);
-
+ static FCDECL0(void, FlushPECaches);
//
// instance FCALLs
//
QCFuncElement("TraceAssemblyResolveHandlerInvoked", AssemblyNative::TraceAssemblyResolveHandlerInvoked)
QCFuncElement("TraceAssemblyLoadFromResolveHandlerInvoked", AssemblyNative::TraceAssemblyLoadFromResolveHandlerInvoked)
QCFuncElement("TraceSatelliteSubdirectoryPathProbed", AssemblyNative::TraceSatelliteSubdirectoryPathProbed)
+ FCFuncElement("FlushPECaches", AssemblyNative::FlushPECaches)
FCFuncEnd()
FCFuncStart(gAssemblyNameFuncs)
}
}
+void PEImage::CleanAllReadOnlyPages()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ CrstHolder holder(&s_hashLock);
+
+ for (PtrHashMap::PtrIterator i = s_Images->begin(); !i.end(); ++i)
+ {
+ PEImage *image = (PEImage*) i.GetValue();
+ PEImageLayoutHolder pLayout(image->GetLayout(PEImageLayout::LAYOUT_ANY, 0));
+ if ((PEImageLayout*)pLayout != NULL)
+ {
+ pLayout->CleanAllReadOnlyPages();
+ }
+ }
+}
+
PEImage::~PEImage()
{
CONTRACTL
BOOL IsOpened();
BOOL HasLoadedLayout();
+ static void CleanAllReadOnlyPages();
+
public:
// ------------------------------------------------------------
// Public API
}
}
+/*
+ CleanAllReadOnlyPages is called for PEImageLayout of each PEImage,
+ and it traverses all relocations and calls MAPMarkDontNeedCleanPages for all memory regions
+ between relocations (if there're no relocations, then simply calls MAPMarkDontNeedCleanPages for whole memory region of dll).
+ MAPMarkDontNeedCleanPages then traverses all views (i.e. mmaped regions) in MappedViewList and finds the ones that intersect with memory region passed in as argument.
+ MappedViewList is global and shared between all mmapping (in other words contains views for all dlls), and also each view has information about access rights.
+ However, pages with relocations might have read-only access rights in views,
+ because in ApplyBaseRelocations before relocation application access rights are saved and then restored after relocation is applied.
+ So, we can't rely on access right that are stored in view (madvise for dirty page will drop it and relocations won't be applied on next page error, so it can lead to sigsegv).
+ On the other hand, some views with write access might be places between relocations, so we have to check access rights to skip writable views.
+
+ This function does similar thing as PEImageLayout::ApplyBaseRelocations for MappedImageLayout.
+ Verify this during rebase. Note that part of logic that does relocations traversal should only be called for images that support it (verify this by rechecking all usages of ApplyBaseRelocations).
+ */
+void PEImageLayout::CleanAllReadOnlyPages()
+{
+ STANDARD_VM_CONTRACT;
+
+#ifdef TARGET_UNIX
+ SSIZE_T delta = (SIZE_T) GetBase() - (SIZE_T) GetPreferredBase();
+ PTR_VOID pBase = GetBase();
+
+ if ((!HasNativeHeader() && !HasReadyToRunHeader())
+ || delta == 0)
+ {
+ PAL_LOADMarkDontNeedCleanPages(pBase, 0, 0);
+ return;
+ }
+
+ LOG((LF_LOADER, LL_INFO100, "PEImage: cleaning read only pages\n"));
+
+ COUNT_T dirSize;
+ TADDR dir = GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_BASERELOC, &dirSize);
+
+ BYTE * pCleanRegion = (BYTE *) pBase;
+ // The page size of PE file relocs is always 4096 bytes
+ const SIZE_T cbPageSize = 4096;
+
+ COUNT_T dirPos = 0;
+ while (dirPos < dirSize)
+ {
+ PIMAGE_BASE_RELOCATION r = (PIMAGE_BASE_RELOCATION)(dir + dirPos);
+
+ COUNT_T fixupsSize = VAL32(r->SizeOfBlock);
+ USHORT *fixups = (USHORT *) (r + 1);
+
+ _ASSERTE(fixupsSize > sizeof(IMAGE_BASE_RELOCATION));
+ _ASSERTE((fixupsSize - sizeof(IMAGE_BASE_RELOCATION)) % 2 == 0);
+
+ COUNT_T fixupsCount = (fixupsSize - sizeof(IMAGE_BASE_RELOCATION)) / 2;
+
+ _ASSERTE((BYTE *)(fixups + fixupsCount) <= (BYTE *)(dir + dirSize));
+
+ DWORD rva = VAL32(r->VirtualAddress);
+
+ BYTE * pageAddress = (BYTE *) pBase + rva;
+ bool coversNextPage = false;
+
+ for (COUNT_T fixupIndex = 0; fixupIndex < fixupsCount && !coversNextPage; fixupIndex++)
+ {
+ USHORT fixup = VAL16(fixups[fixupIndex]);
+
+ BYTE * address = pageAddress + (fixup & 0xfff);
+
+ switch (fixup>>12)
+ {
+ case IMAGE_REL_BASED_PTR:
+ if (address + sizeof(TADDR) > pageAddress + cbPageSize)
+ {
+ coversNextPage = true;
+ }
+ break;
+
+#ifdef TARGET_ARM
+ case IMAGE_REL_BASED_THUMB_MOV32:
+ if (address + 8 > pageAddress + cbPageSize)
+ {
+ coversNextPage = true;
+ }
+ break;
+#endif
+
+ case IMAGE_REL_BASED_ABSOLUTE:
+ //no adjustment
+ break;
+
+ default:
+ _ASSERTE(!"Unhandled reloc type!");
+ }
+ }
+ SIZE_T size = (SIZE_T) (pageAddress - pCleanRegion);
+
+ if (size > 0)
+ {
+ PAL_LOADMarkDontNeedCleanPages(pBase, pCleanRegion, size);
+ }
+
+ pCleanRegion = pageAddress + cbPageSize + (coversNextPage ? cbPageSize : 0);
+
+ dirPos += fixupsSize;
+ }
+ _ASSERTE(dirSize == dirPos);
+
+ PAL_LOADMarkDontNeedCleanPages(pBase, pCleanRegion, 0);
+#endif
+}
+
RawImageLayout::RawImageLayout(const void *flat, COUNT_T size, PEImage* pOwner)
{
void ApplyBaseRelocations(BOOL isRelocated);
+ void CleanAllReadOnlyPages();
+
public:
#ifdef DACCESS_COMPILE
void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
public void StartProfileOptimization(string? profile) { }
public override string ToString() { throw null; }
public void Unload() { }
+ public static void FlushPECaches() { }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public partial struct ContextualReflectionScope : System.IDisposable
{