As far as I understand it, you have to open the file and acquire a file handle which you then map with CreateFileMapping
, which will call NtCreateSection
, which calls MmCreateSection
. If the file is mapped for the first time a new segment object and control area are created first then depending on whether the section is created for a data, image or page-file backed MiCreateDataFileMap
, MiCreateImageFileMap
or MiCreatePagingFileMap
is called.
MiCreateDataFileMap
sets up the subsection object and section object. In the normal case only one subsection is created, but under some special conditions multiple subsections are used, e.g. if the file is very large. For data files, the subsection object field SubsectionBase
is left blank. Instead the SegmentPteTemplate
field of the segment object is setup properly which can be used to create the PPTEs when necessary. This defers the creation of PPTEs until a view is mapped for the first time which avoids wasting memory when very large data files are mapped. Note a PPTE is a PTE that is serving as a prototype PTE, but an _MMPTE_PROTOTYPE
is a PTE that is pointing to a prototype.
MiCreateImageFileMap
creates the section object and loads the PE header of the specified file and verifies it then one subsection is created for the PE header and one for each PE section. If a very small image file is mapped then only one subsection is created for the complete file. Besides the subsections also the related PPTEs for each of them are created and their page protection flags are set according to the protection settings of the related PE section. These PPTEs will be used as a template for building the real PTEs when a view is mapped and accessed.
After a section is created it can be mapped into the address space by creating a view from it. The flProtect
passed to CreateFileMapping
specifies the protection of the section object. All mapped views of the object must be compatible with this protection. You specify dwMaximumSizeLow
and dwMaximumSizeHigh
to be 0 in order for dwMaximumSizeHigh
to be set to the length of the file automatically.
You then pass the returned section object handle to MapViewOfFile
, which will calls NtMapViewOfSection
on it, which calls MmMapViewOfSegment
, which calls MmCreateMemoryArea
, which is where the view is mapped into the VAD of the process with the protection dwDesiredAccess
supplied to MapViewOfFile
, which serves as the protection type for all PTEs that the VAD entry covers. dwNumberOfBytesToMap = 0
and dwFileOffsetLow = 0
in MapViewOfFile
maps the whole file.
When a view is mapped, I believe that all of the PTEs are made to point to the prototype PTEs and are given the protection of the PPTE. For an image file, the PPTEs have already been initialised to subsection PTEs. For a data file, the PPTEs for the view need to be initialised to subsection PTEs. The VAD entry for the view is now created. The VAD entry protection isn't always reflective of the protection of the PTEs it covers, because it can cover multiple subsections and multiple blocks within those subsections.
The first time an address in the mapping is actually accessed, the subsection prototype PTE is filled in on demand with the allocated physical page filled with the I/O write for that range and the process PTE is filled in with that same address. For an image, the PPTE was already filled in when the subsections were created along with protection information derived from the section header characteristics in the image, and it just fills in the PTE with that address and the protection information in it.
When the PTE is trimmed from the process working set, the working set manager accesses the PFN to locate the PPTE address, decreases the share count, and it inserts the PPTE address into the PTE.
I'm not sure when a VAD PTE (which have a prototype bit and prototype address of 0xFFFFFFFF0000
and is not valid) occurs. I would have thought the PPTEs are always there at their virtual address and can be pointed to as soon as the VAD entry is created.