ReactOS 0.4.16-dev-122-g325d74c
section.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * PURPOSE: Implements section objects
21 *
22 * PROGRAMMERS: Rex Jolliff
23 * David Welch
24 * Eric Kohl
25 * Emanuele Aliberti
26 * Eugene Ingerman
27 * Casper Hornstrup
28 * KJK::Hyperion
29 * Guido de Jong
30 * Ge van Geldorp
31 * Royce Mitchell III
32 * Filip Navara
33 * Aleksey Bragin
34 * Jason Filby
35 * Thomas Weidenmueller
36 * Gunnar Andre' Dalsnes
37 * Mike Nordell
38 * Alex Ionescu
39 * Gregor Anich
40 * Steven Edwards
41 * Herve Poussineau
42 */
43
44/* INCLUDES *****************************************************************/
45
46#include <ntoskrnl.h>
47#include <cache/newcc.h>
48#include <cache/section/newmm.h>
49#define NDEBUG
50#include <debug.h>
51#include <reactos/exeformat.h>
52
53#include "ARM3/miarm.h"
54
55#undef MmSetPageEntrySectionSegment
56#define MmSetPageEntrySectionSegment(S,O,E) do { \
57 DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
58 _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__); \
59 } while (0)
60
61extern MMSESSION MmSession;
62
63static LARGE_INTEGER TinyTime = {{-1L, -1L}};
64
65#ifndef NEWCC
67
68VOID
71{
72 //DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
74 Segment->Locked = TRUE;
75}
76
77VOID
80{
81 ASSERT(Segment->Locked);
82 Segment->Locked = FALSE;
84 //DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
85}
86#endif
87
88static
91{
92 KIRQL OldIrql = MiAcquirePfnLock();
94
95 while (TRUE)
96 {
97 Segment = SectionObjectPointer->DataSectionObject;
98 if (!Segment)
99 break;
100
102 {
103 MiReleasePfnLock(OldIrql);
105 OldIrql = MiAcquirePfnLock();
106 continue;
107 }
108
111 break;
112 }
113
114 MiReleasePfnLock(OldIrql);
115
116 return Segment;
117}
118
119/* Somewhat grotesque, but eh... */
121{
122 ASSERT((Segment->SegFlags & MM_DATAFILE_SEGMENT) == 0);
123
124 return CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
125}
126
129 IN PVOID Session,
133
135NTAPI
139 IN PLARGE_INTEGER InputMaximumSize,
144
146NTAPI
157
158//
159// PeFmtCreateSection depends on the following:
160//
163
167
177
178/* TYPES *********************************************************************/
179
180typedef struct
181{
189}
191
192/* GLOBALS *******************************************************************/
193
195
197
199{
200 PAGE_NOACCESS, /* 0 = NONE */
201 PAGE_NOACCESS, /* 1 = SHARED */
202 PAGE_EXECUTE, /* 2 = EXECUTABLE */
203 PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
204 PAGE_READONLY, /* 4 = READABLE */
205 PAGE_READONLY, /* 5 = READABLE, SHARED */
206 PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
207 PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
208 /*
209 * FIXME? do we really need the WriteCopy field in segments? can't we use
210 * PAGE_WRITECOPY here?
211 */
212 PAGE_READWRITE, /* 8 = WRITABLE */
213 PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
214 PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
215 PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
216 PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
217 PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
218 PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
219 PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
220};
221
224{
229};
230
231
232/* FUNCTIONS *****************************************************************/
233
234
235
237NTAPI
239 LONGLONG SegOffset,
241/*
242 * FUNCTION: write a page for a section backed memory area.
243 * PARAMETERS:
244 * MemoryArea - Memory area to write the page for.
245 * Offset - Offset of the page to write.
246 * Page - Page which contains the data to write.
247 */
248{
252 UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
253 PMDL Mdl = (PMDL)MdlBase;
254 PFILE_OBJECT FileObject = Segment->FileObject;
256
257 FileOffset.QuadPart = Segment->Image.FileOffset + SegOffset;
258
259 RtlZeroMemory(MdlBase, sizeof(MdlBase));
262 Mdl->MdlFlags |= MDL_PAGES_LOCKED;
263
266 if (Status == STATUS_PENDING)
267 {
269 Status = IoStatus.Status;
270 }
271 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
272 {
273 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
274 }
275
276 return Status;
277}
278
279
280/*
281 References:
282 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
283 File Format Specification", revision 6.0 (February 1999)
284*/
286 IN SIZE_T FileHeaderSize,
287 IN PVOID File,
288 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
290 IN PEXEFMT_CB_READ_FILE ReadFileCb,
291 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
292{
293 NTSTATUS nStatus;
294 ULONG cbFileHeaderOffsetSize = 0;
295 ULONG cbSectionHeadersOffset = 0;
296 ULONG cbSectionHeadersSize;
297 ULONG cbSectionHeadersOffsetSize = 0;
298 ULONG cbOptHeaderSize;
299 ULONG cbHeadersSize = 0;
300 ULONG nSectionAlignment;
301 ULONG nFileAlignment;
302 ULONG_PTR ImageBase = 0;
303 const IMAGE_DOS_HEADER * pidhDosHeader;
304 const IMAGE_NT_HEADERS32 * pinhNtHeader;
305 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
306 const IMAGE_SECTION_HEADER * pishSectionHeaders;
307 PMM_SECTION_SEGMENT pssSegments;
308 LARGE_INTEGER lnOffset;
310 SIZE_T nPrevVirtualEndOfSegment = 0;
311 ULONG nFileSizeOfHeaders = 0;
312 ULONG i;
313 ULONG AlignedLength;
314
315 ASSERT(FileHeader);
316 ASSERT(FileHeaderSize > 0);
317 ASSERT(File);
318 ASSERT(ImageSectionObject);
319 ASSERT(ReadFileCb);
320 ASSERT(AllocateSegmentsCb);
321
322 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
323
324 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
325
326#define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
327
328 pBuffer = NULL;
329 pidhDosHeader = FileHeader;
330
331 /* DOS HEADER */
333
334 /* image too small to be an MZ executable */
335 if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
336 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
337
338 /* no MZ signature */
339 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
340 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
341
342 /* NT HEADER */
344
345 /* not a Windows executable */
346 if(pidhDosHeader->e_lfanew <= 0)
347 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
348
349 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
350 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
351
352 if(FileHeaderSize < cbFileHeaderOffsetSize)
353 pinhNtHeader = NULL;
354 else
355 {
356 /*
357 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
358 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
359 */
360 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
361 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
362 }
363
364 /*
365 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
366 * need to read the header from the file
367 */
368 if(FileHeaderSize < cbFileHeaderOffsetSize ||
369 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
370 {
371 ULONG cbNtHeaderSize;
372 ULONG cbReadSize;
373 PVOID pData;
374
375l_ReadHeaderFromFile:
376 cbNtHeaderSize = 0;
377 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
378
379 /* read the header from the file */
380 nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
381
382 if(!NT_SUCCESS(nStatus))
383 {
384 NTSTATUS ReturnedStatus = nStatus;
385
386 /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
387 if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
388
389 DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
390 }
391
392 ASSERT(pData);
394 ASSERT(cbReadSize > 0);
395
397
398 /* the buffer doesn't contain the file header */
399 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
400 DIE(("The file doesn't contain the PE file header\n"));
401
402 pinhNtHeader = pData;
403
404 /* object still not aligned: copy it to the beginning of the buffer */
405 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
406 {
408 RtlMoveMemory(pBuffer, pData, cbReadSize);
409 pinhNtHeader = pBuffer;
410 }
411
412 /* invalid NT header */
414
415 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
416 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
417
419
420 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
421 DIE(("The full NT header is too large\n"));
422
423 /* the buffer doesn't contain the whole NT header */
424 if(cbReadSize < cbNtHeaderSize)
425 DIE(("The file doesn't contain the full NT header\n"));
426 }
427 else
428 {
429 ULONG cbOptHeaderOffsetSize = 0;
430
432
433 /* don't trust an invalid NT header */
434 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
435 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
436
437 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
438 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
439
441
442 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
443 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
444
445 /* the buffer doesn't contain the whole NT header: read it from the file */
446 if(cbOptHeaderOffsetSize > FileHeaderSize)
447 goto l_ReadHeaderFromFile;
448 }
449
450 /* read information from the NT header */
451 piohOptHeader = &pinhNtHeader->OptionalHeader;
452 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
453
455
456 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
457 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
458
459 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
460
461 switch(piohOptHeader->Magic)
462 {
464#ifndef _WIN64
466 DIE(("Win64 optional header, unsupported\n"));
467#else
468 // Fall through.
469#endif
471 break;
472 default:
473 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
474 }
475
476 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
477 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
478 {
479 /* See [1], section 3.4.2 */
480 if(piohOptHeader->SectionAlignment < PAGE_SIZE)
481 {
482 if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
483 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
484 }
485 else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
486 DIE(("The section alignment is smaller than the file alignment\n"));
487
488 nSectionAlignment = piohOptHeader->SectionAlignment;
489 nFileAlignment = piohOptHeader->FileAlignment;
490
491 if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
492 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
493 }
494 else
495 {
496 nSectionAlignment = PAGE_SIZE;
497 nFileAlignment = PAGE_SIZE;
498 }
499
500 ASSERT(IsPowerOf2(nSectionAlignment));
501 ASSERT(IsPowerOf2(nFileAlignment));
502
503 switch(piohOptHeader->Magic)
504 {
505 /* PE32 */
507 {
508 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
509 ImageBase = piohOptHeader->ImageBase;
510
511 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
512 ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
513
514 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
515 ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
516
517 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
518 ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
519
520 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
521 {
522 ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
523
524 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
525 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
526 {
527 ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
528 ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
529 }
530 }
531
532 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
533 {
534 ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
535 piohOptHeader->AddressOfEntryPoint);
536 }
537
538 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
539 ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
540 else
541 ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
542
543 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
544 {
545 if (piohOptHeader->AddressOfEntryPoint == 0)
546 {
547 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
548 }
549 }
550
551 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
552 ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
553
554 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
555 {
556 ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
557
558 /*
559 * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
560 * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
561 * magic to any binary.
562 *
563 * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
564 * but honestly that's not tested. It will also break them when running no ReactOS once we implement
565 * the SxS support -- at which point, duh, this should be removed.
566 *
567 * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
568 */
569 ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
570 }
571
572 break;
573 }
574#ifdef _WIN64
575 /* PE64 */
577 {
578 const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
579
580 pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
581
582 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
583 {
584 ImageBase = pioh64OptHeader->ImageBase;
585 if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
586 DIE(("ImageBase exceeds the address space\n"));
587 }
588
589 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
590 {
591 if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
592 DIE(("SizeOfImage exceeds the address space\n"));
593
594 ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
595 }
596
597 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
598 {
599 if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
600 DIE(("SizeOfStackReserve exceeds the address space\n"));
601
602 ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
603 }
604
605 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
606 {
607 if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
608 DIE(("SizeOfStackCommit exceeds the address space\n"));
609
610 ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
611 }
612
613 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
614 {
615 ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
616
617 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
618 RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
619 {
620 ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
621 ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
622 }
623 }
624
625 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
626 {
627 ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
628 pioh64OptHeader->AddressOfEntryPoint);
629 }
630
631 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
632 ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
633 else
634 ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
635
636 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
637 {
638 if (pioh64OptHeader->AddressOfEntryPoint == 0)
639 {
640 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
641 }
642 }
643
644 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
645 ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
646
647 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
648 ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
649
650 break;
651 }
652#endif // _WIN64
653 }
654
655 /* [1], section 3.4.2 */
656 if((ULONG_PTR)ImageBase % 0x10000)
657 DIE(("ImageBase is not aligned on a 64KB boundary"));
658
659 ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
660 ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
661 ImageSectionObject->ImageInformation.GpValue = 0;
662 ImageSectionObject->ImageInformation.ZeroBits = 0;
663 ImageSectionObject->BasedAddress = (PVOID)ImageBase;
664
665 /* SECTION HEADERS */
667
668 /* see [1], section 3.3 */
669 if(pinhNtHeader->FileHeader.NumberOfSections > 96)
670 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
671
672 /*
673 * the additional segment is for the file's headers. They need to be present for
674 * the benefit of the dynamic loader (to locate exports, defaults for thread
675 * parameters, resources, etc.)
676 */
677 ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
678
679 /* file offset for the section headers */
680 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
681 DIE(("Offset overflow\n"));
682
683 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
684 DIE(("Offset overflow\n"));
685
686 /* size of the section headers */
688 cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
689
690 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
691 DIE(("Section headers too large\n"));
692
693 /* size of the executable's headers */
694 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
695 {
696// if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
697// DIE(("SizeOfHeaders is not aligned\n"));
698
699 if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
700 DIE(("The section headers overflow SizeOfHeaders\n"));
701
702 cbHeadersSize = piohOptHeader->SizeOfHeaders;
703 }
704 else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
705 DIE(("Overflow aligning the size of headers\n"));
706
707 if(pBuffer)
708 {
710 pBuffer = NULL;
711 }
712 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
713 /* WARNING: piohOptHeader IS NO LONGER USABLE */
714 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
715
716 if(FileHeaderSize < cbSectionHeadersOffsetSize)
717 pishSectionHeaders = NULL;
718 else
719 {
720 /*
721 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
722 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
723 */
724 ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
725 pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
726 }
727
728 /*
729 * the buffer doesn't contain the section headers, or the alignment is wrong:
730 * read the headers from the file
731 */
732 if(FileHeaderSize < cbSectionHeadersOffsetSize ||
733 (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
734 {
735 PVOID pData;
736 ULONG cbReadSize;
737
738 lnOffset.QuadPart = cbSectionHeadersOffset;
739
740 /* read the header from the file */
741 nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
742
743 if(!NT_SUCCESS(nStatus))
744 DIE(("ReadFile failed with status %08X\n", nStatus));
745
746 ASSERT(pData);
748 ASSERT(cbReadSize > 0);
749
751
752 /* the buffer doesn't contain all the section headers */
753 if(cbReadSize < cbSectionHeadersSize)
754 DIE(("The file doesn't contain all of the section headers\n"));
755
756 pishSectionHeaders = pData;
757
758 /* object still not aligned: copy it to the beginning of the buffer */
759 if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
760 {
762 RtlMoveMemory(pBuffer, pData, cbReadSize);
763 pishSectionHeaders = pBuffer;
764 }
765 }
766
767 /* SEGMENTS */
768 /* allocate the segments */
770 ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
771
772 if(ImageSectionObject->Segments == NULL)
773 DIE(("AllocateSegments failed\n"));
774
775 /* initialize the headers segment */
776 pssSegments = ImageSectionObject->Segments;
777
778// ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
779
780 if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
781 DIE(("Cannot align the size of the section headers\n"));
782
783 nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
784 if (nPrevVirtualEndOfSegment < cbHeadersSize)
785 DIE(("Cannot align the size of the section headers\n"));
786
787 pssSegments[0].Image.FileOffset = 0;
788 pssSegments[0].Protection = PAGE_READONLY;
789 pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
790 pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
791 pssSegments[0].Image.VirtualAddress = 0;
792 pssSegments[0].Image.Characteristics = 0;
793 pssSegments[0].WriteCopy = TRUE;
794
795 /* skip the headers segment */
796 ++ pssSegments;
797
799
800 ASSERT(ImageSectionObject->RefCount > 0);
801
802 /* convert the executable sections into segments. See also [1], section 4 */
803 for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
804 {
805 ULONG nCharacteristics;
806
807 /* validate the alignment */
808 if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
809 DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
810
811 /* sections must be contiguous, ordered by base address and non-overlapping */
812 if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
813 DIE(("Memory gap between section %u and the previous\n", i));
814
815 /* ignore explicit BSS sections */
816 if(pishSectionHeaders[i].PointerToRawData != 0 && pishSectionHeaders[i].SizeOfRawData != 0)
817 {
818 /* validate the alignment */
819#if 0
820 /* Yes, this should be a multiple of FileAlignment, but there's
821 * stuff out there that isn't. We can cope with that
822 */
823 if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
824 DIE(("SizeOfRawData[%u] is not aligned\n", i));
825#endif
826
827// if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
828// DIE(("PointerToRawData[%u] is not aligned\n", i));
829
830 if(!Intsafe_CanAddULong32(pishSectionHeaders[i].PointerToRawData, pishSectionHeaders[i].SizeOfRawData))
831 DIE(("SizeOfRawData[%u] too large\n", i));
832
833 /* conversion */
834 pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
835 pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
836 }
837 else
838 {
839 /* FIXME: Should reset PointerToRawData to 0 in the image mapping */
840 ASSERT(pssSegments[i].Image.FileOffset == 0);
841 ASSERT(pssSegments[i].RawLength.QuadPart == 0);
842 }
843
844 ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
845
846 nCharacteristics = pishSectionHeaders[i].Characteristics;
847
848 /* no explicit protection */
849 if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
850 {
851 if(nCharacteristics & IMAGE_SCN_CNT_CODE)
852 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
853
854 if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
855 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
856
857 if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
858 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
859 }
860
861 /* see table above */
862 pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
863 pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
864
865 if(pishSectionHeaders[i].Misc.VirtualSize == 0)
866 pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
867 else
868 pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
869
870 AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
871 if(AlignedLength < pssSegments[i].Length.LowPart)
872 DIE(("Cannot align the virtual size of section %u\n", i));
873
874 pssSegments[i].Length.LowPart = AlignedLength;
875
876 if(pssSegments[i].Length.QuadPart == 0)
877 DIE(("Virtual size of section %u is null\n", i));
878
879 pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
880 pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
881
882 /* ensure the memory image is no larger than 4GB */
883 nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
884 if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
885 DIE(("The image is too large\n"));
886 }
887
888 if(nSectionAlignment >= PAGE_SIZE)
890
891 /* Success */
892 nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
893
894l_Return:
895 if(pBuffer)
897
898 return nStatus;
899}
900
901/*
902 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
903 * ARGUMENTS: PFILE_OBJECT to wait for.
904 * RETURNS: Status of the wait.
905 */
908{
909 return STATUS_SUCCESS;
910 //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
911}
912
913
914
915VOID
916NTAPI
918{
922 SWAPENTRY SavedSwapEntry;
924
925 Page = 0;
926
928
929 Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
930 for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
931 {
933 if (Entry)
934 {
937 {
939 }
940 else
941 {
943 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
944 if (SavedSwapEntry != 0)
945 {
947 MmFreeSwapPage(SavedSwapEntry);
948 }
950 }
951 }
952 }
953
955}
956
957static
958VOID
959NTAPI
961{
964
966
968
970
971 /* This must be either a valid entry or nothing */
973
974 /* There should be no reference anymore */
976
978 /* If there is a page, this must be because it's still dirty */
979 ASSERT(Page != 0);
980
981 /* Write the page */
982 if (IS_DIRTY_SSE(Entry))
983 MiWritePage(Segment, Offset->QuadPart, Page);
984
986}
987
993VOID
994NTAPI
995MmDereferenceSegmentWithLock(
998{
999 /* Lock the PFN lock because we mess around with SectionObjectPointers */
1000 if (OldIrql == MM_NOIRQL)
1001 {
1002 OldIrql = MiAcquirePfnLock();
1003 }
1004
1005 if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
1006 {
1007 /* Nothing to do yet */
1008 MiReleasePfnLock(OldIrql);
1009 return;
1010 }
1011
1012 *Segment->Flags |= MM_SEGMENT_INDELETE;
1013
1014 /* Flush the segment */
1015 if (*Segment->Flags & MM_DATAFILE_SEGMENT)
1016 {
1017 MiReleasePfnLock(OldIrql);
1018 /* Free the page table. This will flush any remaining dirty data */
1020
1021 OldIrql = MiAcquirePfnLock();
1022 /* Delete the pointer on the file */
1023 ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == Segment);
1024 Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
1025 MiReleasePfnLock(OldIrql);
1026 ObDereferenceObject(Segment->FileObject);
1027
1029 }
1030 else
1031 {
1032 /* Most grotesque thing ever */
1033 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
1034 PMM_SECTION_SEGMENT SectionSegments;
1035 ULONG NrSegments;
1036 ULONG i;
1037
1038 /* Delete the pointer on the file */
1039 ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject);
1040 ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
1041 MiReleasePfnLock(OldIrql);
1042
1043 ObDereferenceObject(ImageSectionObject->FileObject);
1044
1045 NrSegments = ImageSectionObject->NrSegments;
1046 SectionSegments = ImageSectionObject->Segments;
1047 for (i = 0; i < NrSegments; i++)
1048 {
1049 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
1050 {
1051 MmpFreePageFileSegment(&SectionSegments[i]);
1052 }
1053
1054 MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
1055 }
1056
1058 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
1059 }
1060}
1061
1062VOID
1063NTAPI
1066{
1068
1070 if (Entry == 0)
1071 {
1072 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
1073 KeBugCheck(MEMORY_MANAGEMENT);
1074 }
1076 {
1077 DPRINT1("Maximum share count reached\n");
1078 KeBugCheck(MEMORY_MANAGEMENT);
1079 }
1081 {
1082 KeBugCheck(MEMORY_MANAGEMENT);
1083 }
1085}
1086
1087BOOLEAN
1088NTAPI
1092 BOOLEAN Dirty,
1094 ULONG_PTR *InEntry)
1095{
1098 BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
1099 SWAPENTRY SwapEntry;
1100
1101 if (Entry == 0)
1102 {
1103 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
1104 KeBugCheck(MEMORY_MANAGEMENT);
1105 }
1106 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
1107 {
1108 DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
1109 KeBugCheck(MEMORY_MANAGEMENT);
1110 }
1112 {
1113 KeBugCheck(MEMORY_MANAGEMENT);
1114 }
1116 if (Dirty) Entry = DIRTY_SSE(Entry);
1117
1118 /* If we are paging-out, pruning the page for real will be taken care of in MmCheckDirtySegment */
1119 if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
1120 {
1121 /* Update the page mapping in the segment and we're done */
1123 return FALSE;
1124 }
1125
1126 /* We are pruning the last mapping on this page. See if we can keep it a bit more. */
1127 ASSERT(!PageOut);
1128
1129 if (IsDataMap)
1130 {
1131 /* We can always keep memory in for data maps */
1133 return FALSE;
1134 }
1135
1136 if (!FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
1137 {
1138 ASSERT(Segment->WriteCopy);
1141 /* So this must have been a read-only page. Keep it ! */
1143 return FALSE;
1144 }
1145
1146 /*
1147 * So this is a page for a shared section of a DLL.
1148 * We can keep it if it is not dirty.
1149 */
1150 SwapEntry = MmGetSavedSwapEntryPage(Page);
1151 if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
1152 {
1154 return FALSE;
1155 }
1156
1157 /* No more processes are referencing this shared dirty page. Ditch it. */
1158 if (SwapEntry)
1159 {
1161 MmFreeSwapPage(SwapEntry);
1162 }
1165 return TRUE;
1166}
1167
1168static
1170MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
1171{
1173 KIRQL Irql;
1174 PVOID DestAddress;
1175
1177 DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1178 if (DestAddress == NULL)
1179 {
1180 return STATUS_NO_MEMORY;
1181 }
1182 ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
1183 ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
1184 RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
1185 MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1186 return STATUS_SUCCESS;
1187}
1188
1189static
1191NTAPI
1196 _In_opt_ PLARGE_INTEGER ValidDataLength,
1197 _In_ BOOLEAN SetDirty)
1198{
1199 /* Let's use a 64K granularity. */
1200 LONGLONG RangeStart, RangeEnd;
1202 PFILE_OBJECT FileObject = Segment->FileObject;
1203
1204 /* Calculate our range, aligned on 64K if possible. */
1205 Status = RtlLongLongAdd(Offset, Length, &RangeEnd);
1207 if (!NT_SUCCESS(Status))
1208 return Status;
1209
1210 /* If the file is not random access and we are not the page out thread
1211 * read a 64K Chunk. */
1213 && !FlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
1214 {
1215 RangeStart = Offset - (Offset % _64K);
1216 if (RangeEnd % _64K)
1217 RangeEnd += _64K - (RangeEnd % _64K);
1218 }
1219 else
1220 {
1221 RangeStart = Offset - (Offset % PAGE_SIZE);
1222 if (RangeEnd % PAGE_SIZE)
1223 RangeEnd += PAGE_SIZE - (RangeEnd % PAGE_SIZE);
1224 }
1225
1226 /* Clamp if needed */
1227 if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
1228 {
1229 if (RangeEnd > Segment->RawLength.QuadPart)
1230 RangeEnd = Segment->RawLength.QuadPart;
1231 }
1232
1233 /* Let's gooooooooo */
1234 for ( ; RangeStart < RangeEnd; RangeStart += _64K)
1235 {
1236 /* First take a look at where we miss pages */
1237 ULONG ToReadPageBits = 0;
1238 LONGLONG ChunkEnd = RangeStart + _64K;
1239
1240 if (ChunkEnd > RangeEnd)
1241 ChunkEnd = RangeEnd;
1242
1244 for (LONGLONG ChunkOffset = RangeStart; ChunkOffset < ChunkEnd; ChunkOffset += PAGE_SIZE)
1245 {
1246 LARGE_INTEGER CurrentOffset;
1247
1248 CurrentOffset.QuadPart = ChunkOffset;
1250
1251 /* Let any pending read proceed */
1252 while (MM_IS_WAIT_PTE(Entry))
1253 {
1255
1257
1259 Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1260 }
1261
1262 if (Entry != 0)
1263 {
1264 /* Dirtify it if it's a resident page and we're asked to */
1265 if (SetDirty && !IS_SWAP_FROM_SSE(Entry))
1267 continue;
1268 }
1269
1270 ToReadPageBits |= 1UL << ((ChunkOffset - RangeStart) >> PAGE_SHIFT);
1271
1272 /* Put a wait entry here */
1273 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1274 }
1276
1277 if (ToReadPageBits == 0)
1278 {
1279 /* Nothing to do for this chunk */
1280 continue;
1281 }
1282
1283 /* Now perform the actual read */
1284 LONGLONG ChunkOffset = RangeStart;
1285 while (ChunkOffset < ChunkEnd)
1286 {
1287 /* Move forward if there is a hole */
1288 ULONG BitSet;
1289 if (!_BitScanForward(&BitSet, ToReadPageBits))
1290 {
1291 /* Nothing more to read */
1292 break;
1293 }
1294 ToReadPageBits >>= BitSet;
1295 ChunkOffset += BitSet * PAGE_SIZE;
1296 ASSERT(ChunkOffset < ChunkEnd);
1297
1298 /* Get the range we have to read */
1299 _BitScanForward(&BitSet, ~ToReadPageBits);
1300 ULONG ReadLength = BitSet * PAGE_SIZE;
1301
1303
1304 /* Clamp (This is for image mappings */
1305 if ((ChunkOffset + ReadLength) > ChunkEnd)
1306 ReadLength = ChunkEnd - ChunkOffset;
1307
1308 ASSERT(ReadLength != 0);
1309
1310 /* Allocate a MDL */
1312 if (!Mdl)
1313 {
1314 /* Damn. Roll-back. */
1316 while (ChunkOffset < ChunkEnd)
1317 {
1318 if (ToReadPageBits & 1)
1319 {
1320 LARGE_INTEGER CurrentOffset;
1321 CurrentOffset.QuadPart = ChunkOffset;
1323 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1324 }
1325 ToReadPageBits >>= 1;
1326 ChunkOffset += PAGE_SIZE;
1327 }
1330 }
1331
1332 /* Get our pages */
1335 for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1336 {
1338 if (!NT_SUCCESS(Status))
1339 {
1340 /* Damn. Roll-back. */
1341 for (UINT j = 0; j < i; j++)
1343 goto Failed;
1344 }
1345 }
1346
1347 Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ;
1348
1350 FileOffset.QuadPart = Segment->Image.FileOffset + ChunkOffset;
1351
1352 /* Clamp to VDL */
1353 if (ValidDataLength && ((FileOffset.QuadPart + ReadLength) > ValidDataLength->QuadPart))
1354 {
1355 if (FileOffset.QuadPart > ValidDataLength->QuadPart)
1356 {
1357 /* Great, nothing to read. */
1358 goto AssignPagesToSegment;
1359 }
1360
1361 Mdl->Size = (FileOffset.QuadPart + ReadLength) - ValidDataLength->QuadPart;
1362 }
1363
1364 KEVENT Event;
1366
1367 /* Disable APCs */
1368 KIRQL OldIrql;
1370
1373 if (Status == STATUS_PENDING)
1374 {
1376 Status = Iosb.Status;
1377 }
1378
1379 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
1380 {
1381 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
1382 }
1383
1385
1387 {
1388 DPRINT1("Got STATUS_END_OF_FILE at offset %I64d for file %wZ.\n", FileOffset.QuadPart, &FileObject->FileName);
1390 }
1391
1392 if (!NT_SUCCESS(Status))
1393 {
1394 /* Damn. Roll back. */
1395 for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1397
1398Failed:
1400 while (ChunkOffset < ChunkEnd)
1401 {
1402 if (ToReadPageBits & 1)
1403 {
1404 LARGE_INTEGER CurrentOffset;
1405 CurrentOffset.QuadPart = ChunkOffset;
1407 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1408 }
1409 ToReadPageBits >>= 1;
1410 ChunkOffset += PAGE_SIZE;
1411 }
1413 IoFreeMdl(Mdl);;
1414 return Status;
1415 }
1416
1417AssignPagesToSegment:
1419
1420 for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1421 {
1422 ULONG_PTR Entry = MAKE_SSE(Pages[i] << PAGE_SHIFT, 0);
1423 LARGE_INTEGER CurrentOffset;
1424 CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
1425
1427
1428 if (SetDirty)
1430
1431 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, Entry);
1432 }
1433
1435
1436 IoFreeMdl(Mdl);
1437 ToReadPageBits >>= BitSet;
1438 ChunkOffset += BitSet * PAGE_SIZE;
1439 }
1440 }
1441
1442 return STATUS_SUCCESS;
1443}
1444
1445static VOID
1449 ULONG OldType,
1450 ULONG OldProtect,
1451 ULONG NewType,
1453{
1456 BOOLEAN DoCOW = FALSE;
1457 ULONG i;
1459
1462 Segment = MemoryArea->SectionData.Segment;
1464
1465 if ((Segment->WriteCopy) &&
1467 {
1468 DoCOW = TRUE;
1469 }
1470
1471 if (OldProtect != NewProtect)
1472 {
1473 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1474 {
1475 SWAPENTRY SwapEntry;
1476 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1478
1479 /* Wait for a wait entry to disappear */
1480 do
1481 {
1482 MmGetPageFileMapping(Process, Address, &SwapEntry);
1483 if (SwapEntry != MM_WAIT_ENTRY)
1484 break;
1490 }
1491 while (TRUE);
1492
1493 /*
1494 * If we doing COW for this segment then check if the page is
1495 * already private.
1496 */
1498 {
1502
1504 + MemoryArea->SectionData.ViewOffset;
1506 /*
1507 * An MM_WAIT_ENTRY is ok in this case... It'll just count as
1508 * IS_SWAP_FROM_SSE and we'll do the right thing.
1509 */
1511
1512 /* Choose protection based on what was requested */
1515 else
1517
1519 {
1521 }
1522 }
1523
1525 {
1527 Protect);
1528 }
1529 }
1530 }
1531
1533}
1534
1536NTAPI
1539 PVOID Address,
1541{
1547 ULONG_PTR Entry1;
1550 BOOLEAN HasSwapEntry;
1551 PVOID PAddress;
1553 SWAPENTRY SwapEntry;
1554
1555 ASSERT(Locked);
1556
1557 /*
1558 * There is a window between taking the page fault and locking the
1559 * address space when another thread could load the page so we check
1560 * that.
1561 */
1563 {
1564 return STATUS_SUCCESS;
1565 }
1566
1568 {
1570 }
1571
1572 /*
1573 * Check for the virtual memory area being deleted.
1574 */
1576 {
1577 return STATUS_UNSUCCESSFUL;
1578 }
1579
1580 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1581 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1582 + MemoryArea->SectionData.ViewOffset;
1583
1584 Segment = MemoryArea->SectionData.Segment;
1586 &MemoryArea->SectionData.RegionListHead,
1587 Address, NULL);
1588 ASSERT(Region != NULL);
1589
1590 /* Check for a NOACCESS mapping */
1591 if (Region->Protect & PAGE_NOACCESS)
1592 {
1594 }
1595
1596 if (Region->Protect & PAGE_GUARD)
1597 {
1598 /* Remove it */
1600 &MemoryArea->SectionData.RegionListHead,
1601 Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1603
1604 if (!NT_SUCCESS(Status))
1605 {
1606 DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1607 }
1608
1610 }
1611
1612 HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1613
1614 /* See if we should use a private page */
1615 if (HasSwapEntry)
1616 {
1617 SWAPENTRY DummyEntry;
1618
1619 MmGetPageFileMapping(Process, Address, &SwapEntry);
1620 if (SwapEntry == MM_WAIT_ENTRY)
1621 {
1626 }
1627
1629 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1630 if (!Process) MI_SET_PROCESS2("Kernel Section");
1632 if (!NT_SUCCESS(Status))
1633 {
1634 return STATUS_NO_MEMORY;
1635 }
1636
1637 /*
1638 * Must be private page we have swapped out.
1639 */
1640
1641 /*
1642 * Sanity check
1643 */
1645 ASSERT(DummyEntry == SwapEntry);
1646
1647 /* Tell everyone else we are serving the fault. */
1648 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1649
1651
1652 Status = MmReadFromSwapPage(SwapEntry, Page);
1653 if (!NT_SUCCESS(Status))
1654 {
1655 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1656 KeBugCheck(MEMORY_MANAGEMENT);
1657 }
1658
1660 MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1661 ASSERT(DummyEntry == MM_WAIT_ENTRY);
1662
1664 PAddress,
1665 Region->Protect,
1666 Page);
1667 if (!NT_SUCCESS(Status))
1668 {
1669 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1670 KeBugCheck(MEMORY_MANAGEMENT);
1671 return Status;
1672 }
1673
1674 /*
1675 * Store the swap entry for later use.
1676 */
1677 MmSetSavedSwapEntryPage(Page, SwapEntry);
1678
1679 /*
1680 * Add the page to the process's working set
1681 */
1683 /*
1684 * Finish the operation
1685 */
1686 DPRINT("Address 0x%p\n", Address);
1687 return STATUS_SUCCESS;
1688 }
1689
1690 /*
1691 * Lock the segment
1692 */
1694
1695 /*
1696 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1697 */
1698 if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
1699 {
1701 /*
1702 * Just map the desired physical page
1703 */
1704 Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1706 PAddress,
1707 Region->Protect,
1708 Page);
1709 if (!NT_SUCCESS(Status))
1710 {
1711 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1712 KeBugCheck(MEMORY_MANAGEMENT);
1713 return Status;
1714 }
1715
1716 /*
1717 * Cleanup and release locks
1718 */
1719 DPRINT("Address 0x%p\n", Address);
1720 return STATUS_SUCCESS;
1721 }
1722
1723 /*
1724 * Check if this page needs to be mapped COW
1725 */
1726 if ((Segment->WriteCopy) &&
1727 (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))
1728 {
1730 }
1731 else
1732 {
1733 Attributes = Region->Protect;
1734 }
1735
1736
1737 /*
1738 * Get the entry corresponding to the offset within the section
1739 */
1741 if (Entry == 0)
1742 {
1743 /*
1744 * If the entry is zero, then we need to load the page.
1745 */
1746 if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
1747 {
1748 /* We are beyond the data which is on file. Just get a new page. */
1750 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1751 if (!Process) MI_SET_PROCESS2("Kernel Section");
1753 if (!NT_SUCCESS(Status))
1754 {
1756 return STATUS_NO_MEMORY;
1757 }
1760
1762 if (!NT_SUCCESS(Status))
1763 {
1764 DPRINT1("Unable to create virtual mapping\n");
1765 KeBugCheck(MEMORY_MANAGEMENT);
1766 }
1767 ASSERT(MmIsPagePresent(Process, PAddress));
1768 if (Process)
1770
1771 DPRINT("Address 0x%p\n", Address);
1772 return STATUS_SUCCESS;
1773 }
1774
1777
1778 /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
1780
1781 PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
1782
1784
1785 FsRtlReleaseFile(Segment->FileObject);
1786
1787 /* Lock address space again */
1789 if (!NT_SUCCESS(Status))
1790 {
1791 if (Status == STATUS_NO_MEMORY)
1792 {
1793 return Status;
1794 }
1795 /* Damn */
1796 DPRINT1("Failed to page data in!\n");
1797 return STATUS_IN_PAGE_ERROR;
1798 }
1799
1800 /* Everything went fine. Restart the operation */
1802 }
1803 else if (IS_SWAP_FROM_SSE(Entry))
1804 {
1805 SWAPENTRY SwapEntry;
1806
1807 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1808
1809 /* See if a page op is running on this segment. */
1810 if (SwapEntry == MM_WAIT_ENTRY)
1811 {
1817 }
1818
1820 if (!NT_SUCCESS(Status))
1821 {
1823 return STATUS_NO_MEMORY;
1824 }
1825
1826 /*
1827 * Release all our locks and read in the page from disk
1828 */
1831
1833
1834 Status = MmReadFromSwapPage(SwapEntry, Page);
1835 if (!NT_SUCCESS(Status))
1836 {
1837 KeBugCheck(MEMORY_MANAGEMENT);
1838 }
1839
1840 /*
1841 * Relock the address space and segment
1842 */
1845
1846 /*
1847 * Check the entry. No one should change the status of a page
1848 * that has a pending page-in.
1849 */
1851 if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY))
1852 {
1853 DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1854 KeBugCheck(MEMORY_MANAGEMENT);
1855 }
1856
1857 /*
1858 * Save the swap entry.
1859 */
1860 MmSetSavedSwapEntryPage(Page, SwapEntry);
1861
1862 /* Map the page into the process address space */
1864 PAddress,
1865 Attributes,
1866 Page);
1867 if (!NT_SUCCESS(Status))
1868 {
1869 DPRINT1("Unable to create virtual mapping\n");
1870 KeBugCheck(MEMORY_MANAGEMENT);
1871 }
1872 if (Process)
1874
1875 /*
1876 * Mark the offset within the section as having valid, in-memory
1877 * data
1878 */
1879 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1882
1883 DPRINT("Address 0x%p\n", Address);
1884 return STATUS_SUCCESS;
1885 }
1886 else
1887 {
1888 /* We already have a page on this section offset. Map it into the process address space. */
1890
1892 PAddress,
1893 Attributes,
1894 Page);
1895 if (!NT_SUCCESS(Status))
1896 {
1897 DPRINT1("Unable to create virtual mapping\n");
1898 KeBugCheck(MEMORY_MANAGEMENT);
1899 }
1900
1901 if (Process)
1903
1904 /* Take a reference on it */
1907
1908 DPRINT("Address 0x%p\n", Address);
1909 return STATUS_SUCCESS;
1910 }
1911}
1912
1914NTAPI
1917 PVOID Address,
1919{
1921 PFN_NUMBER OldPage;
1922 PFN_NUMBER NewPage;
1923 PFN_NUMBER UnmappedPage;
1924 PVOID PAddress;
1929 BOOLEAN Cow = FALSE;
1931 BOOLEAN Unmapped;
1933
1934 DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1935
1936 /* Get the region for this address */
1938 &MemoryArea->SectionData.RegionListHead,
1939 Address, NULL);
1940 ASSERT(Region != NULL);
1941 if (!(Region->Protect & PAGE_IS_WRITABLE))
1943
1944 /* Make sure we have a page mapping for this address. */
1946 {
1948 if (!NT_SUCCESS(Status))
1949 {
1950 /* This is invalid access ! */
1951 return Status;
1952 }
1953 }
1954
1955 /*
1956 * Check if the page has already been set readwrite
1957 */
1959 {
1960 DPRINT("Address 0x%p\n", Address);
1961 return STATUS_SUCCESS;
1962 }
1963
1964 /* Check if we are doing Copy-On-Write */
1965 Segment = MemoryArea->SectionData.Segment;
1966 Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1967
1968 if (!Cow)
1969 {
1970 /* Simply update page protection and we're done */
1972 return STATUS_SUCCESS;
1973 }
1974
1975 /* Calculate the new protection & check if we should update the region */
1976 NewProtect = Region->Protect;
1978 {
1979 NewProtect &= ~PAGE_IS_WRITECOPY;
1980 if (Region->Protect & PAGE_IS_EXECUTABLE)
1982 else
1985 &MemoryArea->SectionData.RegionListHead,
1988 }
1989
1990 /*
1991 * Find the offset of the page
1992 */
1993 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1994 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1995 + MemoryArea->SectionData.ViewOffset;
1996
1997 /* Get the page mapping this section offset. */
2000
2001 /* Get the current page mapping for the process */
2002 ASSERT(MmIsPagePresent(Process, PAddress));
2003 OldPage = MmGetPfnForProcess(Process, PAddress);
2004 ASSERT(OldPage != 0);
2005
2006 if (IS_SWAP_FROM_SSE(Entry) ||
2007 PFN_FROM_SSE(Entry) != OldPage)
2008 {
2010 /* This is a private page. We must only change the page protection. */
2012 return STATUS_SUCCESS;
2013 }
2014
2015 /*
2016 * Allocate a page
2017 */
2019 if (!NT_SUCCESS(Status))
2020 {
2022 return STATUS_NO_MEMORY;
2023 }
2024
2025 /*
2026 * Copy the old page
2027 */
2028 NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
2029
2030 /*
2031 * Unshare the old page.
2032 */
2033 DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
2034 Unmapped = MmDeleteVirtualMapping(Process, PAddress, NULL, &UnmappedPage);
2035 if (!Unmapped || (UnmappedPage != OldPage))
2036 {
2037 /* Uh , we had a page just before, but suddenly it changes. Someone corrupted us. */
2038 KeBugCheckEx(MEMORY_MANAGEMENT,
2040 (ULONG_PTR)PAddress,
2041 (ULONG_PTR)__FILE__,
2042 __LINE__);
2043 }
2044
2045 if (Process)
2046 MmDeleteRmap(OldPage, Process, PAddress);
2049
2050 /*
2051 * Set the PTE to point to the new page
2052 */
2053 if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2054 {
2055 DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2056 KeBugCheck(MEMORY_MANAGEMENT);
2057 }
2058
2059 if (Process)
2060 MmInsertRmap(NewPage, Process, PAddress);
2061
2062 DPRINT("Address 0x%p\n", Address);
2063 return STATUS_SUCCESS;
2064}
2065
2067NTAPI
2071 SIZE_T Length,
2072 ULONG Protect,
2073 PULONG OldProtect)
2074{
2077 ULONG_PTR MaxLength;
2078
2080 if (Length > MaxLength)
2081 Length = (ULONG)MaxLength;
2082
2084 &MemoryArea->SectionData.RegionListHead,
2085 BaseAddress, NULL);
2086 ASSERT(Region != NULL);
2087
2088 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2089 Region->Protect != Protect)
2090 {
2092 }
2093
2094 *OldProtect = Region->Protect;
2096 &MemoryArea->SectionData.RegionListHead,
2099
2100 return Status;
2101}
2102
2105 PVOID Address,
2108{
2110 PVOID RegionBaseAddress;
2112
2114 &MemoryArea->SectionData.RegionListHead,
2115 Address, &RegionBaseAddress);
2116 if (Region == NULL)
2117 {
2118 return STATUS_UNSUCCESSFUL;
2119 }
2120
2122 {
2123 Segment = MemoryArea->SectionData.Segment;
2124 Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2125 Info->Type = MEM_IMAGE;
2126 }
2127 else
2128 {
2129 Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2130 Info->Type = MEM_MAPPED;
2131 }
2132 Info->BaseAddress = RegionBaseAddress;
2134 Info->RegionSize = Region->Length;
2135 Info->State = MEM_COMMIT;
2136 Info->Protect = Region->Protect;
2137
2139 return STATUS_SUCCESS;
2140}
2141
2142VOID NTAPI
2144{
2145 PSECTION Section = ObjectBody;
2146
2147 /* Check if it's an ARM3, or ReactOS section */
2148 if (!MiIsRosSectionObject(Section))
2149 {
2150 MiDeleteARM3Section(ObjectBody);
2151 return;
2152 }
2153
2154 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2155 if (Section->u.Flags.Image)
2156 {
2157 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2158
2159 /*
2160 * NOTE: Section->ImageSection can be NULL for short time
2161 * during the section creating. If we fail for some reason
2162 * until the image section is properly initialized we shouldn't
2163 * process further here.
2164 */
2165 if (Section->Segment == NULL)
2166 return;
2167
2168 KIRQL OldIrql = MiAcquirePfnLock();
2169 ImageSectionObject->SectionCount--;
2170
2171 /* We just dereference the first segment */
2172 ASSERT(ImageSectionObject->RefCount > 0);
2173 /* MmDereferenceSegmentWithLock releases PFN lock */
2174 MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
2175 }
2176 else
2177 {
2179
2180 /*
2181 * NOTE: Section->Segment can be NULL for short time
2182 * during the section creating.
2183 */
2184 if (Segment == NULL)
2185 return;
2186
2187 KIRQL OldIrql = MiAcquirePfnLock();
2188 Segment->SectionCount--;
2189
2190 /* MmDereferenceSegmentWithLock releases PFN lock */
2191 MmDereferenceSegmentWithLock(Segment, OldIrql);
2192 }
2193}
2194
2195VOID NTAPI
2197 IN PVOID Object,
2200 IN ULONG SystemHandleCount)
2201{
2202 DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2203}
2204
2205CODE_SEG("INIT")
2207NTAPI
2209{
2210 PSECTION PhysSection;
2213 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2214 LARGE_INTEGER SectionSize;
2215 HANDLE Handle;
2217
2218 /*
2219 * Create the section mapping physical memory
2220 */
2223 &Name,
2225 NULL,
2226 NULL);
2227 /*
2228 * Create the Object
2229 */
2232 &Obj,
2234 NULL,
2235 sizeof(*PhysSection),
2236 0,
2237 0,
2238 (PVOID*)&PhysSection);
2239 if (!NT_SUCCESS(Status))
2240 {
2241 DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2242 return Status;
2243 }
2244
2245 /*
2246 * Initialize it
2247 */
2248 RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2249
2250 /* Mark this as a "ROS Section" */
2251 PhysSection->u.Flags.filler = 1;
2253 PhysSection->u.Flags.PhysicalMemory = 1;
2254 PhysSection->SizeOfSection = SectionSize;
2257 if (Segment == NULL)
2258 {
2259 ObDereferenceObject(PhysSection);
2260 return STATUS_NO_MEMORY;
2261 }
2263 PhysSection->Segment = (PSEGMENT)Segment;
2264 Segment->RefCount = 1;
2265
2266 Segment->ReferenceCount = &Segment->RefCount;
2267 Segment->Flags = &Segment->SegFlags;
2268
2270 Segment->Image.FileOffset = 0;
2271 Segment->Protection = PAGE_EXECUTE_READWRITE;
2272 Segment->RawLength = SectionSize;
2273 Segment->Length = SectionSize;
2275 Segment->WriteCopy = FALSE;
2276 Segment->Image.VirtualAddress = 0;
2277 Segment->Image.Characteristics = 0;
2279
2280 Status = ObInsertObject(PhysSection,
2281 NULL,
2283 0,
2284 NULL,
2285 &Handle);
2286 if (!NT_SUCCESS(Status))
2287 {
2288 ObDereferenceObject(PhysSection);
2289 return Status;
2290 }
2292
2293 return STATUS_SUCCESS;
2294}
2295
2296CODE_SEG("INIT")
2298NTAPI
2300{
2301 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2303
2304 DPRINT("Creating Section Object Type\n");
2305
2306 /* Initialize the section based root */
2309
2310 /* Initialize the Section object type */
2311 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2312 RtlInitUnicodeString(&Name, L"Section");
2313 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2314 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2315 ObjectTypeInitializer.PoolType = PagedPool;
2316 ObjectTypeInitializer.UseDefaultObject = TRUE;
2317 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2318 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2319 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2320 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2321 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2322 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2323
2325
2326 return STATUS_SUCCESS;
2327}
2328
2329static
2331NTAPI
2335 PLARGE_INTEGER UMaximumSize,
2339 BOOLEAN GotFileHandle)
2340/*
2341 * Create a section backed by a data file
2342 */
2343{
2344 PSECTION Section;
2348 KIRQL OldIrql;
2349
2350 /*
2351 * Create the section
2352 */
2357 NULL,
2358 sizeof(*Section),
2359 0,
2360 0,
2361 (PVOID*)&Section);
2362 if (!NT_SUCCESS(Status))
2363 {
2364 return Status;
2365 }
2366 /*
2367 * Initialize it
2368 */
2369 RtlZeroMemory(Section, sizeof(*Section));
2370
2371 /* Mark this as a "ROS" section */
2372 Section->u.Flags.filler = 1;
2374 Section->u.Flags.File = 1;
2375
2377 Section->u.Flags.NoChange = 1;
2379 Section->u.Flags.Reserve = 1;
2380
2381 if (!GotFileHandle)
2382 {
2383 ASSERT(UMaximumSize != NULL);
2384 // ASSERT(UMaximumSize->QuadPart != 0);
2385 MaximumSize = *UMaximumSize;
2386 }
2387 else
2388 {
2391 if (!NT_SUCCESS(Status))
2392 {
2393 ObDereferenceObject(Section);
2394 return Status;
2395 }
2396
2397 /*
2398 * FIXME: Revise this once a locking order for file size changes is
2399 * decided
2400 */
2401 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2402 {
2403 MaximumSize = *UMaximumSize;
2404 }
2405 else
2406 {
2408 /* Mapping zero-sized files isn't allowed. */
2409 if (MaximumSize.QuadPart == 0)
2410 {
2411 ObDereferenceObject(Section);
2413 }
2414 }
2415
2416 if (MaximumSize.QuadPart > FileSize.QuadPart)
2417 {
2420 sizeof(LARGE_INTEGER),
2421 &MaximumSize);
2422 if (!NT_SUCCESS(Status))
2423 {
2424 ObDereferenceObject(Section);
2426 }
2427 }
2428 }
2429
2430 if (FileObject->SectionObjectPointer == NULL)
2431 {
2432 ObDereferenceObject(Section);
2434 }
2435
2436 /*
2437 * Lock the file
2438 */
2440 if (Status != STATUS_SUCCESS)
2441 {
2442 ObDereferenceObject(Section);
2443 return Status;
2444 }
2445
2446 /* Lock the PFN lock while messing with Section Object pointers */
2447grab_segment:
2448 OldIrql = MiAcquirePfnLock();
2449 Segment = FileObject->SectionObjectPointer->DataSectionObject;
2450
2451 while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2452 {
2453 MiReleasePfnLock(OldIrql);
2455 OldIrql = MiAcquirePfnLock();
2456 Segment = FileObject->SectionObjectPointer->DataSectionObject;
2457 }
2458
2459 /*
2460 * If this file hasn't been mapped as a data file before then allocate a
2461 * section segment to describe the data file mapping
2462 */
2463 if (Segment == NULL)
2464 {
2465 /* Release the lock. ExAllocatePoolWithTag might acquire it */
2466 MiReleasePfnLock(OldIrql);
2467
2470 if (Segment == NULL)
2471 {
2472 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2473 ObDereferenceObject(Section);
2474 return STATUS_NO_MEMORY;
2475 }
2476
2477 /* We are creating it */
2478 RtlZeroMemory(Segment, sizeof(*Segment));
2480 Segment->RefCount = 1;
2481
2482 /* Acquire lock again */
2483 OldIrql = MiAcquirePfnLock();
2484
2485 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
2486 {
2487 /* Well that's bad luck. Restart it all over */
2488 MiReleasePfnLock(OldIrql);
2490 goto grab_segment;
2491 }
2492
2493 FileObject->SectionObjectPointer->DataSectionObject = Segment;
2494
2495 /* We're safe to release the lock now */
2496 MiReleasePfnLock(OldIrql);
2497
2498 Section->Segment = (PSEGMENT)Segment;
2499
2500 /* Self-referencing segment */
2501 Segment->Flags = &Segment->SegFlags;
2502 Segment->ReferenceCount = &Segment->RefCount;
2503
2504 Segment->SectionCount = 1;
2505
2507 Segment->FileObject = FileObject;
2509
2510 Segment->Image.FileOffset = 0;
2511 Segment->Protection = SectionPageProtection;
2512
2513 Segment->Image.Characteristics = 0;
2516 {
2517 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2518 }
2519 else
2520 {
2521 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2522 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2523 }
2524 Segment->Image.VirtualAddress = 0;
2526
2527 /* We're good to use it now */
2528 OldIrql = MiAcquirePfnLock();
2529 Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2530 MiReleasePfnLock(OldIrql);
2531 }
2532 else
2533 {
2534 Section->Segment = (PSEGMENT)Segment;
2535 InterlockedIncrement64(&Segment->RefCount);
2536 InterlockedIncrementUL(&Segment->SectionCount);
2537
2538 MiReleasePfnLock(OldIrql);
2539
2541
2542 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2544 {
2545 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2546 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2547 }
2548
2550 }
2551 Section->SizeOfSection = MaximumSize;
2552
2553 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2554 *SectionObject = Section;
2555 return STATUS_SUCCESS;
2556}
2557
2558/*
2559 TODO: not that great (declaring loaders statically, having to declare all of
2560 them, having to keep them extern, etc.), will fix in the future
2561*/
2563(
2564 IN CONST VOID * FileHeader,
2565 IN SIZE_T FileHeaderSize,
2566 IN PVOID File,
2567 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2569 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2570 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2571);
2572
2574(
2575 IN CONST VOID * FileHeader,
2576 IN SIZE_T FileHeaderSize,
2577 IN PVOID File,
2578 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2580 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2581 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2582);
2583
2585{
2587#ifdef __ELF
2589#endif
2590};
2591
2592static
2594NTAPI
2596{
2597 SIZE_T SizeOfSegments;
2598 PMM_SECTION_SEGMENT Segments;
2599
2600 /* TODO: check for integer overflow */
2601 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2602
2604 SizeOfSegments,
2606
2607 if(Segments)
2608 RtlZeroMemory(Segments, SizeOfSegments);
2609
2610 return Segments;
2611}
2612static
2614NTAPI
2617 IN ULONG Length,
2618 OUT PVOID * Data,
2619 OUT PVOID * AllocBase,
2620 OUT PULONG ReadSize)
2621{
2624 ULONG AdjustOffset;
2625 ULONG OffsetAdjustment;
2627 ULONG UsedSize;
2628 PVOID Buffer;
2631
2633
2634 if(Length == 0)
2635 {
2636 KeBugCheck(MEMORY_MANAGEMENT);
2637 }
2638
2639 FileOffset = *Offset;
2640
2641 /* Negative/special offset: it cannot be used in this context */
2642 if(FileOffset.u.HighPart < 0)
2643 {
2644 KeBugCheck(MEMORY_MANAGEMENT);
2645 }
2646
2647 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2648 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2649 FileOffset.u.LowPart = AdjustOffset;
2650
2651 BufferSize = Length + OffsetAdjustment;
2653
2654 /*
2655 * It's ok to use paged pool, because this is a temporary buffer only used in
2656 * the loading of executables. The assumption is that MmCreateSection is
2657 * always called at low IRQLs and that these buffers don't survive a brief
2658 * initialization phase
2659 */
2661 if (!Buffer)
2662 {
2664 }
2665
2667
2668 UsedSize = (ULONG)Iosb.Information;
2669
2670 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2671 {
2674 }
2675
2676 if(NT_SUCCESS(Status))
2677 {
2678 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2679 *AllocBase = Buffer;
2680 *ReadSize = UsedSize - OffsetAdjustment;
2681 }
2682 else
2683 {
2684 ExFreePoolWithTag(Buffer, 'rXmM');
2685 }
2686
2687 return Status;
2688}
2689
2690#ifdef NASSERT
2691# define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2692# define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2693# define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2694#else
2695static
2696VOID
2697NTAPI
2699{
2700 ULONG i;
2701
2702 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2703 {
2704 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2705 ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2706 }
2707}
2708
2709static
2710VOID
2711NTAPI
2713{
2714 ULONG i;
2715
2716 MmspAssertSegmentsSorted(ImageSectionObject);
2717
2718 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2719 {
2720 ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2721
2722 if(i > 0)
2723 {
2724 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2725 (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2726 ImageSectionObject->Segments[i - 1].Length.QuadPart));
2727 }
2728 }
2729}
2730
2731static
2732VOID
2733NTAPI
2735{
2736 ULONG i;
2737
2738 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2739 {
2740 ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2741 ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2742 }
2743}
2744#endif
2745
2746static
2747int
2748__cdecl
2750 const void * y)
2751{
2752 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2753 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2754
2755 if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2756 return 1;
2757 else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2758 return -1;
2759 else
2760 return 0;
2761}
2762
2763/*
2764 * Ensures an image section's segments are sorted in memory
2765 */
2766static
2767VOID
2768NTAPI
2770 IN ULONG Flags)
2771{
2773 {
2774 MmspAssertSegmentsSorted(ImageSectionObject);
2775 }
2776 else
2777 {
2778 qsort(ImageSectionObject->Segments,
2779 ImageSectionObject->NrSegments,
2780 sizeof(ImageSectionObject->Segments[0]),
2782 }
2783}
2784
2785
2786/*
2787 * Ensures an image section's segments don't overlap in memory and don't have
2788 * gaps and don't have a null size. We let them map to overlapping file regions,
2789 * though - that's not necessarily an error
2790 */
2791static
2792BOOLEAN
2793NTAPI
2795(
2796 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2797 IN ULONG Flags
2798)
2799{
2800 ULONG i;
2801
2803 {
2804 MmspAssertSegmentsNoOverlap(ImageSectionObject);
2805 return TRUE;
2806 }
2807
2808 ASSERT(ImageSectionObject->NrSegments >= 1);
2809
2810 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2811 {
2812 if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2813 {
2814 return FALSE;
2815 }
2816
2817 if(i > 0)
2818 {
2819 /*
2820 * TODO: relax the limitation on gaps. For example, gaps smaller than a
2821 * page could be OK (Windows seems to be OK with them), and larger gaps
2822 * could lead to image sections spanning several discontiguous regions
2823 * (NtMapViewOfSection could then refuse to map them, and they could
2824 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2825 */
2826 if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2827 ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2828 ImageSectionObject->Segments[i].Image.VirtualAddress)
2829 {
2830 return FALSE;
2831 }
2832 }
2833 }
2834
2835 return TRUE;
2836}
2837
2838/*
2839 * Merges and pads an image section's segments until they all are page-aligned
2840 * and have a size that is a multiple of the page size
2841 */
2842static
2843BOOLEAN
2844NTAPI
2846(
2847 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2848 IN ULONG Flags
2849)
2850{
2851 ULONG i;
2852 ULONG LastSegment;
2853 PMM_SECTION_SEGMENT EffectiveSegment;
2854
2856 {
2857 MmspAssertSegmentsPageAligned(ImageSectionObject);
2858 return TRUE;
2859 }
2860
2861 LastSegment = 0;
2862 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2863
2864 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2865 {
2866 /*
2867 * The first segment requires special handling
2868 */
2869 if (i == 0)
2870 {
2872 ULONG_PTR VirtualOffset;
2873
2874 VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2875
2876 /* Round down the virtual address to the nearest page */
2877 EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2878
2879 /* Round up the virtual size to the nearest page */
2880 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2881 EffectiveSegment->Image.VirtualAddress;
2882
2883 /* Adjust the raw address and size */
2884 VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2885
2886 if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2887 {
2888 return FALSE;
2889 }
2890
2891 /*
2892 * Garbage in, garbage out: unaligned base addresses make the file
2893 * offset point in curious and odd places, but that's what we were
2894 * asked for
2895 */
2896 EffectiveSegment->Image.FileOffset -= VirtualOffset;
2897 EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2898 }
2899 else
2900 {
2901 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2902 ULONG_PTR EndOfEffectiveSegment;
2903
2904 EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2905 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2906
2907 /*
2908 * The current segment begins exactly where the current effective
2909 * segment ended, therefore beginning a new effective segment
2910 */
2911 if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2912 {
2913 LastSegment ++;
2914 ASSERT(LastSegment <= i);
2915 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2916
2917 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2918
2919 if (LastSegment != i)
2920 {
2921 /*
2922 * Copy the current segment. If necessary, the effective segment
2923 * will be expanded later
2924 */
2925 *EffectiveSegment = *Segment;
2926 }
2927
2928 /*
2929 * Page-align the virtual size. We know for sure the virtual address
2930 * already is
2931 */
2932 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2933 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2934 }
2935 /*
2936 * The current segment is still part of the current effective segment:
2937 * extend the effective segment to reflect this
2938 */
2939 else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2940 {
2941 static const ULONG FlagsToProtection[16] =
2942 {
2959 };
2960
2961 unsigned ProtectionFlags;
2962
2963 /*
2964 * Extend the file size
2965 */
2966
2967 /* Unaligned segments must be contiguous within the file */
2968 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2969 EffectiveSegment->RawLength.QuadPart))
2970 {
2971 return FALSE;
2972 }
2973
2974 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2975
2976 /*
2977 * Extend the virtual size
2978 */
2979 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2980
2981 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2982 EffectiveSegment->Image.VirtualAddress;
2983
2984 /*
2985 * Merge the protection
2986 */
2987 EffectiveSegment->Protection |= Segment->Protection;
2988
2989 /* Clean up redundance */
2990 ProtectionFlags = 0;
2991
2992 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2993 ProtectionFlags |= 1 << 0;
2994
2995 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2996 ProtectionFlags |= 1 << 1;
2997
2998 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2999 ProtectionFlags |= 1 << 2;
3000
3001 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3002 ProtectionFlags |= 1 << 3;
3003
3004 ASSERT(ProtectionFlags < 16);
3005 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3006
3007 /* If a segment was required to be shared and cannot, fail */
3008 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3009 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3010 {
3011 return FALSE;
3012 }
3013 }
3014 /*
3015 * We assume no holes between segments at this point
3016 */
3017 else
3018 {
3019 KeBugCheck(MEMORY_MANAGEMENT);
3020 }
3021 }
3022 }
3023 ImageSectionObject->NrSegments = LastSegment + 1;
3024
3025 return TRUE;
3026}
3027
3030 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3031{
3033 PVOID FileHeader;
3034 PVOID FileHeaderBuffer;
3035 ULONG FileHeaderSize;
3036 ULONG Flags;
3037 ULONG OldNrSegments;
3039 ULONG i;
3040
3041 /*
3042 * Read the beginning of the file (2 pages). Should be enough to contain
3043 * all (or most) of the headers
3044 */
3045 Offset.QuadPart = 0;
3046
3048 &Offset,
3049 PAGE_SIZE * 2,
3050 &FileHeader,
3051 &FileHeaderBuffer,
3052 &FileHeaderSize);
3053
3054 if (!NT_SUCCESS(Status))
3055 return Status;
3056
3057 if (FileHeaderSize == 0)
3058 {
3059 ExFreePool(FileHeaderBuffer);
3060 return STATUS_UNSUCCESSFUL;
3061 }
3062
3063 /*
3064 * Look for a loader that can handle this executable
3065 */
3066 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3067 {
3068 Flags = 0;
3069
3070 Status = ExeFmtpLoaders[i](FileHeader,
3071 FileHeaderSize,
3072 FileObject,
3073 ImageSectionObject,
3074 &Flags,
3077
3078 if (!NT_SUCCESS(Status))
3079 {
3080 if (ImageSectionObject->Segments)
3081 {
3082 ExFreePool(ImageSectionObject->Segments);
3083 ImageSectionObject->Segments = NULL;
3084 }
3085 }
3086
3088 break;
3089 }
3090
3091 ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3092
3093 /*
3094 * No loader handled the format
3095 */
3097 {
3100 }
3101
3102 if (!NT_SUCCESS(Status))
3103 return Status;
3104
3105 ASSERT(ImageSectionObject->Segments != NULL);
3106 ASSERT(ImageSectionObject->RefCount > 0);
3107
3108 /*
3109 * Some defaults
3110 */
3111 /* FIXME? are these values platform-dependent? */
3112 if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3113 ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3114
3115 if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3116 ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3117
3118 if(ImageSectionObject->BasedAddress == NULL)
3119 {
3120 if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3121 ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3122 else
3123 ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3124 }
3125
3126 /*
3127 * And now the fun part: fixing the segments
3128 */
3129
3130 /* Sort them by virtual address */
3131 MmspSortSegments(ImageSectionObject, Flags);
3132
3133 /* Ensure they don't overlap in memory */
3134 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3136
3137 /* Ensure they are aligned */
3138 OldNrSegments = ImageSectionObject->NrSegments;
3139
3140 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3142
3143 /* Trim them if the alignment phase merged some of them */
3144 if (ImageSectionObject->NrSegments < OldNrSegments)
3145 {
3146 PMM_SECTION_SEGMENT Segments;
3147 SIZE_T SizeOfSegments;
3148
3149 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3150
3152 SizeOfSegments,
3154
3155 if (Segments == NULL)
3157
3158 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3159 ExFreePool(ImageSectionObject->Segments);
3160 ImageSectionObject->Segments = Segments;
3161 }
3162
3163 /* And finish their initialization */
3164 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3165 {
3166 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3167 ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3168 ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3169 MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3170 ImageSectionObject->Segments[i].FileObject = FileObject;
3171 }
3172
3173 ASSERT(ImageSectionObject->RefCount > 0);
3174
3175 ImageSectionObject->FileObject = FileObject;
3176
3178 return Status;
3179}
3180
3185 PLARGE_INTEGER UMaximumSize,
3189{
3190 PSECTION Section;
3192 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3193 KIRQL OldIrql;
3194
3195
3196 if (FileObject == NULL)
3198
3199 if (FileObject->SectionObjectPointer == NULL)
3200 {
3201 DPRINT1("Denying section creation due to missing cache initialization\n");
3203 }
3204
3205 /*
3206 * Create the section
3207 */
3212 NULL,
3213 sizeof(*Section),
3214 0,
3215 0,
3216 (PVOID*)(PVOID)&Section);
3217 if (!NT_SUCCESS(Status))
3218 {
3219 return Status;
3220 }
3221
3222 /*
3223 * Initialize it
3224 */
3225 RtlZeroMemory(Section, sizeof(*Section));
3226
3227 /* Mark this as a "ROS" Section */
3228 Section->u.Flags.filler = 1;
3229
3231 Section->u.Flags.File = 1;
3232 Section->u.Flags.Image = 1;
3234 Section->u.Flags.NoChange = 1;
3235
3236grab_image_section_object:
3237 OldIrql = MiAcquirePfnLock();
3238
3239 /* Wait for it to be properly created or deleted */
3240 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3241 while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3242 {
3243 MiReleasePfnLock(OldIrql);
3244
3246
3247 OldIrql = MiAcquirePfnLock();
3248 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3249 }
3250
3251 if (ImageSectionObject == NULL)
3252 {
3253 NTSTATUS StatusExeFmt;
3254
3255 /* Release the lock because ExAllocatePoolWithTag could need to acquire it */
3256 MiReleasePfnLock(OldIrql);
3257
3259 if (ImageSectionObject == NULL)
3260 {
3261 ObDereferenceObject(Section);
3262 return STATUS_NO_MEMORY;
3263 }
3264
3265 ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3266 ImageSectionObject->RefCount = 1;
3267 ImageSectionObject->SectionCount = 1;
3268
3269 OldIrql = MiAcquirePfnLock();
3270 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
3271 {
3272 MiReleasePfnLock(OldIrql);
3273 /* Bad luck. Start over */
3274 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3275 goto grab_image_section_object;
3276 }
3277
3278 FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3279
3280 MiReleasePfnLock(OldIrql);
3281
3282 /* Purge the cache */
3283 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3284
3285 StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3286
3287 if (!NT_SUCCESS(StatusExeFmt))
3288 {
3289 /* Unset */
3290 OldIrql = MiAcquirePfnLock();
3291 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3292 MiReleasePfnLock(OldIrql);
3293
3294 if(ImageSectionObject->Segments != NULL)
3295 ExFreePool(ImageSectionObject->Segments);
3296
3297 /*
3298 * If image file is empty, then return that the file is invalid for section
3299 */
3300 Status = StatusExeFmt;
3301 if (StatusExeFmt == STATUS_END_OF_FILE)
3302 {
3304 }
3305
3306 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3307 ObDereferenceObject(Section);
3308 return Status;
3309 }
3310
3311 Section->Segment = (PSEGMENT)ImageSectionObject;
3312 ASSERT(ImageSectionObject->Segments);
3313 ASSERT(ImageSectionObject->RefCount > 0);
3314
3315 /*
3316 * Lock the file
3317 */
3319 if (!NT_SUCCESS(Status))
3320 {
3321 /* Unset */
3322 OldIrql = MiAcquirePfnLock();
3323 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3324 MiReleasePfnLock(OldIrql);
3325
3326 ExFreePool(ImageSectionObject->Segments);
3327 ExFreePool(ImageSectionObject);
3328 ObDereferenceObject(Section);
3329 return Status;
3330 }
3331
3332 OldIrql = MiAcquirePfnLock();
3333 ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3334
3335 /* Take a ref on the file on behalf of the newly created structure */
3337
3338 MiReleasePfnLock(OldIrql);
3339
3340 Status = StatusExeFmt;
3341 }
3342 else
3343 {
3344 /* If FS driver called for delete, tell them it's not possible anymore. */
3345 ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
3346
3347 /* Take one ref */
3348 InterlockedIncrement64(&ImageSectionObject->RefCount);
3349 ImageSectionObject->SectionCount++;
3350
3351 MiReleasePfnLock(OldIrql);
3352
3353 Section->Segment = (PSEGMENT)ImageSectionObject;
3354
3356 }
3357 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3358 *SectionObject = Section;
3359 ASSERT(ImageSectionObject->RefCount > 0);
3360
3361 return Status;
3362}
3363
3364
3365
3366static NTSTATUS
3369 BOOLEAN AsImage,
3373 ULONG Protect,
3374 LONGLONG ViewOffset,
3376{
3377 PMEMORY_AREA MArea;
3379 ULONG Granularity;
3380
3381 ASSERT(ViewSize != 0);
3382
3383 if (Segment->WriteCopy)
3384 {
3385 /* We have to do this because the not present fault
3386 * and access fault handlers depend on the protection
3387 * that should be granted AFTER the COW fault takes
3388 * place to be in Region->Protect. The not present fault
3389 * handler changes this to the correct protection for COW when
3390 * mapping the pages into the process's address space. If a COW
3391 * fault takes place, the access fault handler sets the page protection
3392 * to these values for the newly copied pages
3393 */
3394 if (Protect == PAGE_WRITECOPY)
3396 else if (Protect == PAGE_EXECUTE_WRITECOPY)
3398 }
3399
3400 if (*BaseAddress == NULL)
3401 Granularity = MM_ALLOCATION_GRANULARITY;
3402 else
3403 Granularity = PAGE_SIZE;
3404
3405#ifdef NEWCC
3406 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3407 {
3409 FileOffset.QuadPart = ViewOffset;
3410 ObReferenceObject(Section);
3412 }
3413#endif
3417 ViewSize,
3418 Protect,
3419 &MArea,
3421 Granularity);
3422 if (!NT_SUCCESS(Status))
3423 {
3424 DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3425 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3426 return Status;
3427 }
3428
3429 InterlockedIncrement64(Segment->ReferenceCount);
3430
3431 MArea->SectionData.Segment = Segment;
3432 MArea->SectionData.ViewOffset = ViewOffset;
3433 if (AsImage)
3434 {
3436 }
3437
3438 MmInitializeRegion(&MArea->SectionData.RegionListHead,
3439 ViewSize, 0, Protect);
3440
3441 return STATUS_SUCCESS;
3442}
3443
3444
3445static VOID
3447 PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3448{
3451 SWAPENTRY SavedSwapEntry;
3455
3458
3460
3462 MemoryArea->SectionData.ViewOffset;
3463
3464 Segment = MemoryArea->SectionData.Segment;
3465
3467 while (Entry && MM_IS_WAIT_PTE(Entry))
3468 {
3471
3473
3477 }
3478
3479 /*
3480 * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3481 */
3482 if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3483 {
3484 if (Page == PFN_FROM_SSE(Entry) && Dirty)
3485 {
3486 ASSERT(SwapEntry == 0);
3487 }
3488 }
3489
3490 if (SwapEntry != 0)
3491 {
3492 /*
3493 * Sanity check
3494 */
3495 MmFreeSwapPage(SwapEntry);
3496 }
3497 else if (Page != 0)
3498 {
3499 if (IS_SWAP_FROM_SSE(Entry) ||
3501 {
3502 ASSERT(Process != NULL);
3503
3504 /*
3505 * Just dereference private pages
3506 */
3507 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3508 if (SavedSwapEntry != 0)
3509 {
3510 MmFreeSwapPage(SavedSwapEntry);
3512 }
3515 }
3516 else
3517 {
3518 if (Process)
3519 {
3521 }
3522
3523 /* We don't dirtify for System Space Maps. We let Cc manage that */
3525 }
3526 }
3527}
3528
3529static NTSTATUS
3532{
3536 PLIST_ENTRY CurrentEntry;
3537 PMM_REGION CurrentRegion;
3538 PLIST_ENTRY RegionListHead;
3539
3541 BaseAddress);
3542 if (MemoryArea == NULL)
3543 {
3544 return STATUS_UNSUCCESSFUL;
3545 }
3546
3547 Segment = MemoryArea->SectionData.Segment;
3548
3549#ifdef NEWCC
3550 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3551 {
3555
3556 return Status;
3557 }
3558#endif
3559
3561
3563
3564 RegionListHead = &MemoryArea->SectionData.RegionListHead;
3565 while (!IsListEmpty(RegionListHead))
3566 {
3567 CurrentEntry = RemoveHeadList(RegionListHead);
3568 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3569 ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3570 }
3571
3572 if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3573 {
3575 MemoryArea,
3576 NULL,
3577 NULL);
3578 }
3579 else
3580 {
3582 MemoryArea,
3584 AddressSpace);
3585 }
3587 MmDereferenceSegment(Segment);
3588 return Status;
3589}
3590
3591/* This functions must be called with a locked address space */
3593NTAPI
3596 IN BOOLEAN SkipDebuggerNotify)
3597{
3601 PVOID ImageBaseAddress = 0;
3602
3603 DPRINT("Opening memory area Process %p BaseAddress %p\n",
3605
3606 ASSERT(Process);
3607
3608 AddressSpace = &Process->Vm;
3609
3611 BaseAddress);
3612 if (MemoryArea == NULL ||
3613#ifdef NEWCC
3614 ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3615#else
3617#endif
3619
3620 {
3622
3623 DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3625 }
3626
3628 {
3629 ULONG i;
3630 ULONG NrSegments;
3631 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3632 PMM_SECTION_SEGMENT SectionSegments;
3634
3635 Segment = MemoryArea->SectionData.Segment;
3636 ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3637 SectionSegments = ImageSectionObject->Segments;
3638 NrSegments = ImageSectionObject->NrSegments;
3639
3641
3642 /* Search for the current segment within the section segments
3643 * and calculate the image base address */
3644 for (i = 0; i < NrSegments; i++)
3645 {
3646 if (Segment == &SectionSegments[i])
3647 {
3648 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3649 break;
3650 }
3651 }
3652 if (i >= NrSegments)
3653 {
3654 KeBugCheck(MEMORY_MANAGEMENT);
3655 }
3656
3657 for (i = 0; i < NrSegments; i++)
3658 {
3659 PVOID SBaseAddress = (PVOID)
3660 ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3661
3662 Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3663 if (!NT_SUCCESS(Status))
3664 {
3665 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3666 SBaseAddress, Process, Status);
3668 }
3669 }
3670 DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer);
3671 InterlockedDecrement(&ImageSectionObject->MapCount);
3672 }
3673 else
3674 {
3676 PMMVAD Vad = &MemoryArea->VadNode;
3677 PCONTROL_AREA ControlArea = Vad->ControlArea;
3680 LARGE_INTEGER ViewOffset;
3681 ViewOffset.QuadPart = MemoryArea->SectionData.ViewOffset;
3682
3683 InterlockedIncrement64(Segment->ReferenceCount);
3684
3685 ViewSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
3686
3688 if (!NT_SUCCESS(Status))
3689 {
3690 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3693 }
3694
3695 /* These might be deleted now */
3696 Vad = NULL;
3697 MemoryArea = NULL;
3698
3700 {
3701 /* Don't bother */
3702 MmDereferenceSegment(Segment);
3703 return STATUS_SUCCESS;
3704 }
3706
3707 FileObject = Segment->FileObject;
3709
3710 /* Don't bother for auto-delete closed file. */
3712 {
3714 MmDereferenceSegment(Segment);
3715 return STATUS_SUCCESS;
3716 }
3717
3718 /*
3719 * Flush only when last mapping is deleted.
3720 * FIXME: Why ControlArea == NULL? Or rather: is ControlArea ever not NULL here?
3721 */
3722 if (ControlArea == NULL || ControlArea->NumberOfMappedViews == 1)
3723 {
3724 while (ViewSize > 0)
3725 {
3726 ULONG FlushSize = min(ViewSize, PAGE_ROUND_DOWN(MAXULONG));
3727 MmFlushSegment(FileObject->SectionObjectPointer,
3728 &ViewOffset,
3729 FlushSize,
3730 NULL);
3731 ViewSize -= FlushSize;
3732 ViewOffset.QuadPart += FlushSize;
3733 }
3734 }
3735
3737 MmDereferenceSegment(Segment);
3738 }
3739
3740 /* Notify debugger */
3741 if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3742
3743 return STATUS_SUCCESS;
3744}
3745
3746
3747
3748
3772NTAPI
3774 _In_ HANDLE SectionHandle,
3775 _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3776 _Out_ PVOID SectionInformation,
3777 _In_ SIZE_T SectionInformationLength,
3779{
3780 PSECTION Section;
3783 PAGED_CODE();
3784
3786 if (PreviousMode != KernelMode)
3787 {
3788 _SEH2_TRY
3789 {
3790 ProbeForWrite(SectionInformation,
3791 SectionInformationLength,
3792 __alignof(ULONG));
3793 if (ResultLength != NULL)
3794 {
3796 sizeof(*ResultLength),
3797 __alignof(SIZE_T));
3798 }
3799 }
3801 {
3803 }
3804 _SEH2_END;
3805 }
3806
3807 if (SectionInformationClass == SectionBasicInformation)
3808 {
3809 if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3810 {
3812 }
3813 }
3814 else if (SectionInformationClass == SectionImageInformation)
3815 {
3816 if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3817 {
3819 }
3820 }
3821 else
3822 {
3824 }
3825
3826 Status = ObReferenceObjectByHandle(SectionHandle,
3830 (PVOID*)(PVOID)&Section,
3831 NULL);
3832 if (!NT_SUCCESS(Status))
3833 {
3834 DPRINT1("Failed to reference section: 0x%lx\n", Status);
3835 return Status;
3836 }
3837
3838 switch(SectionInformationClass)
3839 {
3841 {
3843
3844 Sbi.Size = Section->SizeOfSection;
3845 Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3846
3847 Sbi.Attributes = 0;
3848 if (Section->u.Flags.File)
3849 Sbi.Attributes |= SEC_FILE;
3850 if (Section->u.Flags.Image)
3851 Sbi.Attributes |= SEC_IMAGE;
3852
3853 /* Those are not set *************
3854 if (Section->u.Flags.Commit)
3855 Sbi.Attributes |= SEC_COMMIT;
3856 if (Section->u.Flags.Reserve)
3857 Sbi.Attributes |= SEC_RESERVE;
3858 **********************************/
3859
3860 if (Section->u.Flags.Image)
3861 {
3862 if (MiIsRosSectionObject(Section))
3863 {
3864 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3865 Sbi.BaseAddress = 0;
3866 Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3867 }
3868 else
3869 {
3870 /* Not supported yet */
3871 ASSERT(FALSE);
3872 }
3873 }
3874 else if (MiIsRosSectionObject(Section))
3875 {
3876 Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3877 Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3878 }
3879 else
3880 {
3881 DPRINT1("Unimplemented code path\n");
3882 }
3883
3884 _SEH2_TRY
3885 {
3886 *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3887 if (ResultLength != NULL)
3888 {
3889 *ResultLength = sizeof(Sbi);
3890 }
3891 }
3893 {
3895 }
3896 _SEH2_END;
3897 break;
3898 }
3900 {
3901 if (!Section->u.Flags.Image)
3902 {
3904 }
3905 else if (MiIsRosSectionObject(Section))
3906 {
3907 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3908
3909 _SEH2_TRY
3910 {
3912 *Sii = ImageSectionObject->ImageInformation;
3913 if (ResultLength != NULL)
3914 {
3915 *ResultLength = sizeof(*Sii);
3916 }
3917 }
3919 {
3921 }
3922 _SEH2_END;
3923 }
3924 else
3925 {
3926 _SEH2_TRY
3927 {
3929 *Sii = *Section->Segment->u2.ImageInformation;
3930 if (ResultLength != NULL)
3931 *ResultLength = sizeof(*Sii);
3932 }
3934 {
3936 }
3937 _SEH2_END;
3938 }
3939 break;
3940 }
3941 default:
3942 DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3944 }
3945
3946 ObDereferenceObject(Section);
3947
3948 return Status;
3949}
3950
3951/**********************************************************************
3952 * NAME EXPORTED
3953 * MmMapViewOfSection
3954 *
3955 * DESCRIPTION
3956 * Maps a view of a section into the virtual address space of a
3957 * process.
3958 *
3959 * ARGUMENTS
3960 * Section
3961 * Pointer to the section object.
3962 *
3963 * ProcessHandle
3964 * Pointer to the process.
3965 *
3966 * BaseAddress
3967 * Desired base address (or NULL) on entry;
3968 * Actual base address of the view on exit.
3969 *
3970 * ZeroBits
3971 * Number of high order address bits that must be zero.
3972 *
3973 * CommitSize
3974 * Size in bytes of the initially committed section of
3975 * the view.
3976 *
3977 * SectionOffset
3978 * Offset in bytes from the beginning of the section
3979 * to the beginning of the view.
3980 *
3981 * ViewSize
3982 * Desired length of map (or zero to map all) on entry
3983 * Actual length mapped on exit.
3984 *
3985 * InheritDisposition
3986 * Specified how the view is to be shared with
3987 * child processes.
3988 *
3989 * AllocationType
3990 * Type of allocation for the pages.
3991 *
3992 * Protect
3993 * Protection for the committed region of the view.
3994 *
3995 * RETURN VALUE
3996 * Status.
3997 *
3998 * @implemented
3999 */
4011{
4012 PSECTION Section;
4015 BOOLEAN NotAtBase = FALSE;
4016 BOOLEAN IsAttached = FALSE;
4018
4020 {
4021 DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
4023 Process,
4025 ZeroBits,
4026 CommitSize,
4028 ViewSize,
4031 Protect);
4032 }
4033
4034 ASSERT(Process);
4035
4037 {
4039 }
4040
4042 {
4044 IsAttached = TRUE;
4045 }
4046
4047 /* FIXME: We should keep this, but it would break code checking equality */
4048 Protect &= ~PAGE_NOCACHE;
4049
4050 Section = SectionObject;
4051 AddressSpace = &Process->Vm;
4052
4053 if (Section->u.Flags.NoChange)
4055
4057
4058 if (Section->u.Flags.Image)
4059 {
4060 ULONG i;
4061 ULONG NrSegments;
4062 ULONG_PTR ImageBase;
4063 SIZE_T ImageSize;
4064 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4065 PMM_SECTION_SEGMENT SectionSegments;
4066
4067 ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
4068 SectionSegments = ImageSectionObject->Segments;
4069 NrSegments = ImageSectionObject->NrSegments;
4070
4071 ASSERT(ImageSectionObject->RefCount > 0);
4072
4073 ImageBase = (ULONG_PTR)*BaseAddress;
4074 if (ImageBase == 0)
4075 {
4076 ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
4077 }
4078
4079 ImageSize = 0;
4080 for (i = 0; i < NrSegments; i++)
4081 {
4082 ULONG_PTR MaxExtent;
4083 MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
4084 SectionSegments[i].Length.QuadPart);
4085 ImageSize = max(ImageSize, MaxExtent);
4086 }
4087
4088 ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
4089
4090 /* Check for an illegal base address */
4091 if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
4092 ((ImageBase + ImageSize) < ImageSize))
4093 {
4094 ASSERT(*BaseAddress == NULL);
4095 ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
4097 NotAtBase = TRUE;
4098 }
4099 else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
4100 {
4101 ASSERT(*BaseAddress == NULL);
4102 ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4103 NotAtBase = TRUE;
4104 }
4105
4106 /* Check there is enough space to map the section at that point. */
4108 PAGE_ROUND_UP(ImageSize)) != NULL)
4109 {
4110 /* Fail if the user requested a fixed base address. */
4111 if ((*BaseAddress) != NULL)
4112 {
4114 goto Exit;
4115 }
4116 /* Otherwise find a gap to map the image. */
4118 if (ImageBase == 0)
4119 {
4121 goto Exit;
4122 }
4123 /* Remember that we loaded image at a different base address */
4124 NotAtBase = TRUE;
4125 }
4126
4127 for (i = 0; i < NrSegments; i++)
4128 {
4129 PVOID SBaseAddress = (PVOID)
4130 ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4131 MmLockSectionSegment(&SectionSegments[i]);
4133 TRUE,
4134 &SectionSegments[i],
4135 &SBaseAddress,
4136 SectionSegments[i].Length.QuadPart,
4137 SectionSegments[i].Protection,
4138 0,
4139 0);
4140 MmUnlockSectionSegment(&SectionSegments[i]);
4141 if (!NT_SUCCESS(Status))
4142 {
4143 /* roll-back */
4144 while (i--)
4145 {
4146 SBaseAddress = ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4147 MmLockSectionSegment(&SectionSegments[i]);
4148 MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4149 MmUnlockSectionSegment(&SectionSegments[i]);
4150 }
4151
4152 goto Exit;
4153 }
4154 }
4155
4156 *BaseAddress = (PVOID)ImageBase;
4157 *ViewSize = ImageSize;
4158
4159 DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4160
4161 /* One more map */
4162 InterlockedIncrement(&ImageSectionObject->MapCount);
4163 }
4164 else
4165 {
4167 LONGLONG ViewOffset;
4168
4169 ASSERT(Segment->RefCount > 0);
4170
4171 /* check for write access */
4174 {
4176 goto Exit;
4177 }
4178 /* check for read access */
4181 {
4183 goto Exit;
4184 }
4185 /* check for execute access */
4188 {
4190 goto Exit;
4191 }
4192
4193 if (SectionOffset == NULL)
4194 {
4195 ViewOffset = 0;
4196 }
4197 else
4198 {
4199 ViewOffset = SectionOffset->QuadPart;
4200 }
4201
4202 if ((ViewOffset % PAGE_SIZE) != 0)
4203 {
4205 goto Exit;
4206 }
4207
4208 if ((*ViewSize) == 0)
4209 {
4210 (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4211 }
4212 else if ((ExGetPreviousMode() == UserMode) &&
4213 (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4214 (!Section->u.Flags.Reserve))
4215 {
4216 /* Dubious */
4217 (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4218 }
4219
4221
4224 FALSE,
4225 Segment,
4227 *ViewSize,
4228 Protect,
4229 ViewOffset,
4232 if (!NT_SUCCESS(Status))
4233 {
4234 goto Exit;
4235 }
4236 }
4237
4238 if (NotAtBase)
4240 else
4242
4243Exit:
4244
4246
4247 if (IsAttached)
4248 {
4250 }
4251
4252 return Status;
4253}
4254
4255/*
4256 * @unimplemented
4257 */
4258BOOLEAN
4259NTAPI
4263{
4264 BOOLEAN Ret;
4266
4267 /* Check whether an ImageSectionObject exists */
4268 if (SectionObjectPointer->ImageSectionObject != NULL)
4269 {
4270 DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4271 return FALSE;
4272 }
4273
4275 if (!Segment)
4276 {
4277 /* There is no data section. It's fine to do anything. */
4278 return TRUE;
4279 }
4280
4282 if ((Segment->SectionCount == 0) ||
4283 ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4284 {
4285 /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4286 Ret = TRUE;
4287 }
4288 else if (NewFileSize != NULL)
4289 {
4290 /* We can't shrink, but we can extend */
4291 Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4292#if DBG
4293 if (!Ret)
4294 {
4295 DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4296 }
4297#endif
4298 }
4299 else
4300 {
4301 DPRINT1("ERROR: File can't be truncated because it has references held to its data section\n");
4302 Ret = FALSE;
4303 }
4304
4306 MmDereferenceSegment(Segment);
4307
4308 DPRINT("FIXME: didn't check for outstanding write probes\n");
4309
4310 return Ret;
4311}
4312
4313static
4314BOOLEAN
4316{
4318
4320
4321 /* Loop over all entries */
4322 for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4323 PageTable != NULL;
4325 {
4326 for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4327 {
4328 ULONG_PTR Entry = PageTable->PageEntries[i];
4330
4331 if (!Entry)
4332 continue;
4333
4335 {
4336 /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4338 return FALSE;
4339 }
4340
4341 /* Regular entry */
4344
4345 /* Properly remove using the used API */
4346 Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4349 }
4350 }
4351