ReactOS 0.4.15-dev-8222-g9164419
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
1514 {
1516 }
1517 }
1518
1520 {
1522 Protect);
1523 }
1524 }
1525 }
1526
1528}
1529
1531NTAPI
1534 PVOID Address,
1536{
1542 ULONG_PTR Entry1;
1545 BOOLEAN HasSwapEntry;
1546 PVOID PAddress;
1548 SWAPENTRY SwapEntry;
1549
1550 ASSERT(Locked);
1551
1552 /*
1553 * There is a window between taking the page fault and locking the
1554 * address space when another thread could load the page so we check
1555 * that.
1556 */
1558 {
1559 return STATUS_SUCCESS;
1560 }
1561
1563 {
1565 }
1566
1567 /*
1568 * Check for the virtual memory area being deleted.
1569 */
1571 {
1572 return STATUS_UNSUCCESSFUL;
1573 }
1574
1575 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1576 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1577 + MemoryArea->SectionData.ViewOffset;
1578
1579 Segment = MemoryArea->SectionData.Segment;
1581 &MemoryArea->SectionData.RegionListHead,
1582 Address, NULL);
1583 ASSERT(Region != NULL);
1584
1585 /* Check for a NOACCESS mapping */
1586 if (Region->Protect & PAGE_NOACCESS)
1587 {
1589 }
1590
1591 if (Region->Protect & PAGE_GUARD)
1592 {
1593 /* Remove it */
1595 &MemoryArea->SectionData.RegionListHead,
1596 Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1598
1599 if (!NT_SUCCESS(Status))
1600 {
1601 DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1602 }
1603
1605 }
1606
1607 HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1608
1609 /* See if we should use a private page */
1610 if (HasSwapEntry)
1611 {
1612 SWAPENTRY DummyEntry;
1613
1614 MmGetPageFileMapping(Process, Address, &SwapEntry);
1615 if (SwapEntry == MM_WAIT_ENTRY)
1616 {
1621 }
1622
1624 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1625 if (!Process) MI_SET_PROCESS2("Kernel Section");
1627 if (!NT_SUCCESS(Status))
1628 {
1629 return STATUS_NO_MEMORY;
1630 }
1631
1632 /*
1633 * Must be private page we have swapped out.
1634 */
1635
1636 /*
1637 * Sanity check
1638 */
1640 ASSERT(DummyEntry == SwapEntry);
1641
1642 /* Tell everyone else we are serving the fault. */
1643 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1644
1646
1647 Status = MmReadFromSwapPage(SwapEntry, Page);
1648 if (!NT_SUCCESS(Status))
1649 {
1650 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1651 KeBugCheck(MEMORY_MANAGEMENT);
1652 }
1653
1655 MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1656 ASSERT(DummyEntry == MM_WAIT_ENTRY);
1657
1659 PAddress,
1660 Region->Protect,
1661 Page);
1662 if (!NT_SUCCESS(Status))
1663 {
1664 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1665 KeBugCheck(MEMORY_MANAGEMENT);
1666 return Status;
1667 }
1668
1669 /*
1670 * Store the swap entry for later use.
1671 */
1672 MmSetSavedSwapEntryPage(Page, SwapEntry);
1673
1674 /*
1675 * Add the page to the process's working set
1676 */
1678 /*
1679 * Finish the operation
1680 */
1681 DPRINT("Address 0x%p\n", Address);
1682 return STATUS_SUCCESS;
1683 }
1684
1685 /*
1686 * Lock the segment
1687 */
1689
1690 /*
1691 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1692 */
1693 if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
1694 {
1696 /*
1697 * Just map the desired physical page
1698 */
1699 Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1701 PAddress,
1702 Region->Protect,
1703 Page);
1704 if (!NT_SUCCESS(Status))
1705 {
1706 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1707 KeBugCheck(MEMORY_MANAGEMENT);
1708 return Status;
1709 }
1710
1711 /*
1712 * Cleanup and release locks
1713 */
1714 DPRINT("Address 0x%p\n", Address);
1715 return STATUS_SUCCESS;
1716 }
1717
1718 /*
1719 * Check if this page needs to be mapped COW
1720 */
1721 if ((Segment->WriteCopy) &&
1722 (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))
1723 {
1725 }
1726 else
1727 {
1728 Attributes = Region->Protect;
1729 }
1730
1731
1732 /*
1733 * Get the entry corresponding to the offset within the section
1734 */
1736 if (Entry == 0)
1737 {
1738 /*
1739 * If the entry is zero, then we need to load the page.
1740 */
1741 if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
1742 {
1743 /* We are beyond the data which is on file. Just get a new page. */
1745 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1746 if (!Process) MI_SET_PROCESS2("Kernel Section");
1748 if (!NT_SUCCESS(Status))
1749 {
1751 return STATUS_NO_MEMORY;
1752 }
1755
1757 if (!NT_SUCCESS(Status))
1758 {
1759 DPRINT1("Unable to create virtual mapping\n");
1760 KeBugCheck(MEMORY_MANAGEMENT);
1761 }
1762 ASSERT(MmIsPagePresent(Process, PAddress));
1763 if (Process)
1765
1766 DPRINT("Address 0x%p\n", Address);
1767 return STATUS_SUCCESS;
1768 }
1769
1772
1773 /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
1775
1776 PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
1777
1779
1780 FsRtlReleaseFile(Segment->FileObject);
1781
1782 /* Lock address space again */
1784 if (!NT_SUCCESS(Status))
1785 {
1786 if (Status == STATUS_NO_MEMORY)
1787 {
1788 return Status;
1789 }
1790 /* Damn */
1791 DPRINT1("Failed to page data in!\n");
1792 return STATUS_IN_PAGE_ERROR;
1793 }
1794
1795 /* Everything went fine. Restart the operation */
1797 }
1798 else if (IS_SWAP_FROM_SSE(Entry))
1799 {
1800 SWAPENTRY SwapEntry;
1801
1802 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1803
1804 /* See if a page op is running on this segment. */
1805 if (SwapEntry == MM_WAIT_ENTRY)
1806 {
1812 }
1813
1815 if (!NT_SUCCESS(Status))
1816 {
1818 return STATUS_NO_MEMORY;
1819 }
1820
1821 /*
1822 * Release all our locks and read in the page from disk
1823 */
1826
1828
1829 Status = MmReadFromSwapPage(SwapEntry, Page);
1830 if (!NT_SUCCESS(Status))
1831 {
1832 KeBugCheck(MEMORY_MANAGEMENT);
1833 }
1834
1835 /*
1836 * Relock the address space and segment
1837 */
1840
1841 /*
1842 * Check the entry. No one should change the status of a page
1843 * that has a pending page-in.
1844 */
1846 if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY))
1847 {
1848 DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1849 KeBugCheck(MEMORY_MANAGEMENT);
1850 }
1851
1852 /*
1853 * Save the swap entry.
1854 */
1855 MmSetSavedSwapEntryPage(Page, SwapEntry);
1856
1857 /* Map the page into the process address space */
1859 PAddress,
1860 Attributes,
1861 Page);
1862 if (!NT_SUCCESS(Status))
1863 {
1864 DPRINT1("Unable to create virtual mapping\n");
1865 KeBugCheck(MEMORY_MANAGEMENT);
1866 }
1867 if (Process)
1869
1870 /*
1871 * Mark the offset within the section as having valid, in-memory
1872 * data
1873 */
1874 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1877
1878 DPRINT("Address 0x%p\n", Address);
1879 return STATUS_SUCCESS;
1880 }
1881 else
1882 {
1883 /* We already have a page on this section offset. Map it into the process address space. */
1885
1887 PAddress,
1888 Attributes,
1889 Page);
1890 if (!NT_SUCCESS(Status))
1891 {
1892 DPRINT1("Unable to create virtual mapping\n");
1893 KeBugCheck(MEMORY_MANAGEMENT);
1894 }
1895
1896 if (Process)
1898
1899 /* Take a reference on it */
1902
1903 DPRINT("Address 0x%p\n", Address);
1904 return STATUS_SUCCESS;
1905 }
1906}
1907
1909NTAPI
1912 PVOID Address,
1914{
1916 PFN_NUMBER OldPage;
1917 PFN_NUMBER NewPage;
1918 PFN_NUMBER UnmappedPage;
1919 PVOID PAddress;
1924 BOOLEAN Cow = FALSE;
1926 BOOLEAN Unmapped;
1928
1929 DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1930
1931 /* Get the region for this address */
1933 &MemoryArea->SectionData.RegionListHead,
1934 Address, NULL);
1935 ASSERT(Region != NULL);
1936 if (!(Region->Protect & PAGE_IS_WRITABLE))
1938
1939 /* Make sure we have a page mapping for this address. */
1941 {
1943 if (!NT_SUCCESS(Status))
1944 {
1945 /* This is invalid access ! */
1946 return Status;
1947 }
1948 }
1949
1950 /*
1951 * Check if the page has already been set readwrite
1952 */
1954 {
1955 DPRINT("Address 0x%p\n", Address);
1956 return STATUS_SUCCESS;
1957 }
1958
1959 /* Check if we are doing Copy-On-Write */
1960 Segment = MemoryArea->SectionData.Segment;
1961 Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1962
1963 if (!Cow)
1964 {
1965 /* Simply update page protection and we're done */
1967 return STATUS_SUCCESS;
1968 }
1969
1970 /* Calculate the new protection & check if we should update the region */
1971 NewProtect = Region->Protect;
1973 {
1974 NewProtect &= ~PAGE_IS_WRITECOPY;
1975 if (Region->Protect & PAGE_IS_EXECUTABLE)
1977 else
1980 &MemoryArea->SectionData.RegionListHead,
1983 }
1984
1985 /*
1986 * Find the offset of the page
1987 */
1988 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1989 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1990 + MemoryArea->SectionData.ViewOffset;
1991
1992 /* Get the page mapping this section offset. */
1995
1996 /* Get the current page mapping for the process */
1997 ASSERT(MmIsPagePresent(Process, PAddress));
1998 OldPage = MmGetPfnForProcess(Process, PAddress);
1999 ASSERT(OldPage != 0);
2000
2001 if (IS_SWAP_FROM_SSE(Entry) ||
2002 PFN_FROM_SSE(Entry) != OldPage)
2003 {
2005 /* This is a private page. We must only change the page protection. */
2007 return STATUS_SUCCESS;
2008 }
2009
2010 /*
2011 * Allocate a page
2012 */
2014 if (!NT_SUCCESS(Status))
2015 {
2017 return STATUS_NO_MEMORY;
2018 }
2019
2020 /*
2021 * Copy the old page
2022 */
2023 NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
2024
2025 /*
2026 * Unshare the old page.
2027 */
2028 DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
2029 Unmapped = MmDeleteVirtualMapping(Process, PAddress, NULL, &UnmappedPage);
2030 if (!Unmapped || (UnmappedPage != OldPage))
2031 {
2032 /* Uh , we had a page just before, but suddenly it changes. Someone corrupted us. */
2033 KeBugCheckEx(MEMORY_MANAGEMENT,
2035 (ULONG_PTR)PAddress,
2036 (ULONG_PTR)__FILE__,
2037 __LINE__);
2038 }
2039
2040 if (Process)
2041 MmDeleteRmap(OldPage, Process, PAddress);
2044
2045 /*
2046 * Set the PTE to point to the new page
2047 */
2048 if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2049 {
2050 DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2051 KeBugCheck(MEMORY_MANAGEMENT);
2052 }
2053
2054 if (Process)
2055 MmInsertRmap(NewPage, Process, PAddress);
2056
2057 DPRINT("Address 0x%p\n", Address);
2058 return STATUS_SUCCESS;
2059}
2060
2062NTAPI
2066 SIZE_T Length,
2067 ULONG Protect,
2068 PULONG OldProtect)
2069{
2072 ULONG_PTR MaxLength;
2073
2075 if (Length > MaxLength)
2076 Length = (ULONG)MaxLength;
2077
2079 &MemoryArea->SectionData.RegionListHead,
2080 BaseAddress, NULL);
2081 ASSERT(Region != NULL);
2082
2083 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2084 Region->Protect != Protect)
2085 {
2087 }
2088
2089 *OldProtect = Region->Protect;
2091 &MemoryArea->SectionData.RegionListHead,
2094
2095 return Status;
2096}
2097
2100 PVOID Address,
2103{
2105 PVOID RegionBaseAddress;
2107
2109 &MemoryArea->SectionData.RegionListHead,
2110 Address, &RegionBaseAddress);
2111 if (Region == NULL)
2112 {
2113 return STATUS_UNSUCCESSFUL;
2114 }
2115
2117 {
2118 Segment = MemoryArea->SectionData.Segment;
2119 Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2120 Info->Type = MEM_IMAGE;
2121 }
2122 else
2123 {
2124 Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2125 Info->Type = MEM_MAPPED;
2126 }
2127 Info->BaseAddress = RegionBaseAddress;
2129 Info->RegionSize = Region->Length;
2130 Info->State = MEM_COMMIT;
2131 Info->Protect = Region->Protect;
2132
2134 return STATUS_SUCCESS;
2135}
2136
2137VOID NTAPI
2139{
2140 PSECTION Section = ObjectBody;
2141
2142 /* Check if it's an ARM3, or ReactOS section */
2143 if (!MiIsRosSectionObject(Section))
2144 {
2145 MiDeleteARM3Section(ObjectBody);
2146 return;
2147 }
2148
2149 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2150 if (Section->u.Flags.Image)
2151 {
2152 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2153
2154 /*
2155 * NOTE: Section->ImageSection can be NULL for short time
2156 * during the section creating. If we fail for some reason
2157 * until the image section is properly initialized we shouldn't
2158 * process further here.
2159 */
2160 if (Section->Segment == NULL)
2161 return;
2162
2163 KIRQL OldIrql = MiAcquirePfnLock();
2164 ImageSectionObject->SectionCount--;
2165
2166 /* We just dereference the first segment */
2167 ASSERT(ImageSectionObject->RefCount > 0);
2168 /* MmDereferenceSegmentWithLock releases PFN lock */
2169 MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
2170 }
2171 else
2172 {
2174
2175 /*
2176 * NOTE: Section->Segment can be NULL for short time
2177 * during the section creating.
2178 */
2179 if (Segment == NULL)
2180 return;
2181
2182 KIRQL OldIrql = MiAcquirePfnLock();
2183 Segment->SectionCount--;
2184
2185 /* MmDereferenceSegmentWithLock releases PFN lock */
2186 MmDereferenceSegmentWithLock(Segment, OldIrql);
2187 }
2188}
2189
2190VOID NTAPI
2192 IN PVOID Object,
2195 IN ULONG SystemHandleCount)
2196{
2197 DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2198}
2199
2200CODE_SEG("INIT")
2202NTAPI
2204{
2205 PSECTION PhysSection;
2208 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2209 LARGE_INTEGER SectionSize;
2210 HANDLE Handle;
2212
2213 /*
2214 * Create the section mapping physical memory
2215 */
2218 &Name,
2220 NULL,
2221 NULL);
2222 /*
2223 * Create the Object
2224 */
2227 &Obj,
2229 NULL,
2230 sizeof(*PhysSection),
2231 0,
2232 0,
2233 (PVOID*)&PhysSection);
2234 if (!NT_SUCCESS(Status))
2235 {
2236 DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2237 return Status;
2238 }
2239
2240 /*
2241 * Initialize it
2242 */
2243 RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2244
2245 /* Mark this as a "ROS Section" */
2246 PhysSection->u.Flags.filler = 1;
2248 PhysSection->u.Flags.PhysicalMemory = 1;
2249 PhysSection->SizeOfSection = SectionSize;
2252 if (Segment == NULL)
2253 {
2254 ObDereferenceObject(PhysSection);
2255 return STATUS_NO_MEMORY;
2256 }
2258 PhysSection->Segment = (PSEGMENT)Segment;
2259 Segment->RefCount = 1;
2260
2261 Segment->ReferenceCount = &Segment->RefCount;
2262 Segment->Flags = &Segment->SegFlags;
2263
2265 Segment->Image.FileOffset = 0;
2266 Segment->Protection = PAGE_EXECUTE_READWRITE;
2267 Segment->RawLength = SectionSize;
2268 Segment->Length = SectionSize;
2270 Segment->WriteCopy = FALSE;
2271 Segment->Image.VirtualAddress = 0;
2272 Segment->Image.Characteristics = 0;
2274
2275 Status = ObInsertObject(PhysSection,
2276 NULL,
2278 0,
2279 NULL,
2280 &Handle);
2281 if (!NT_SUCCESS(Status))
2282 {
2283 ObDereferenceObject(PhysSection);
2284 return Status;
2285 }
2287
2288 return STATUS_SUCCESS;
2289}
2290
2291CODE_SEG("INIT")
2293NTAPI
2295{
2296 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2298
2299 DPRINT("Creating Section Object Type\n");
2300
2301 /* Initialize the section based root */
2304
2305 /* Initialize the Section object type */
2306 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2307 RtlInitUnicodeString(&Name, L"Section");
2308 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2309 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2310 ObjectTypeInitializer.PoolType = PagedPool;
2311 ObjectTypeInitializer.UseDefaultObject = TRUE;
2312 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2313 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2314 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2315 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2316 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2317 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2318
2320
2321 return STATUS_SUCCESS;
2322}
2323
2324static
2326NTAPI
2330 PLARGE_INTEGER UMaximumSize,
2334 BOOLEAN GotFileHandle)
2335/*
2336 * Create a section backed by a data file
2337 */
2338{
2339 PSECTION Section;
2343 KIRQL OldIrql;
2344
2345 /*
2346 * Create the section
2347 */
2352 NULL,
2353 sizeof(*Section),
2354 0,
2355 0,
2356 (PVOID*)&Section);
2357 if (!NT_SUCCESS(Status))
2358 {
2359 return Status;
2360 }
2361 /*
2362 * Initialize it
2363 */
2364 RtlZeroMemory(Section, sizeof(*Section));
2365
2366 /* Mark this as a "ROS" section */
2367 Section->u.Flags.filler = 1;
2369 Section->u.Flags.File = 1;
2370
2372 Section->u.Flags.NoChange = 1;
2374 Section->u.Flags.Reserve = 1;
2375
2376 if (!GotFileHandle)
2377 {
2378 ASSERT(UMaximumSize != NULL);
2379 // ASSERT(UMaximumSize->QuadPart != 0);
2380 MaximumSize = *UMaximumSize;
2381 }
2382 else
2383 {
2386 if (!NT_SUCCESS(Status))
2387 {
2388 ObDereferenceObject(Section);
2389 return Status;
2390 }
2391
2392 /*
2393 * FIXME: Revise this once a locking order for file size changes is
2394 * decided
2395 */
2396 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2397 {
2398 MaximumSize = *UMaximumSize;
2399 }
2400 else
2401 {
2403 /* Mapping zero-sized files isn't allowed. */
2404 if (MaximumSize.QuadPart == 0)
2405 {
2406 ObDereferenceObject(Section);
2408 }
2409 }
2410
2411 if (MaximumSize.QuadPart > FileSize.QuadPart)
2412 {
2415 sizeof(LARGE_INTEGER),
2416 &MaximumSize);
2417 if (!NT_SUCCESS(Status))
2418 {
2419 ObDereferenceObject(Section);
2421 }
2422 }
2423 }
2424
2425 if (FileObject->SectionObjectPointer == NULL)
2426 {
2427 ObDereferenceObject(Section);
2429 }
2430
2431 /*
2432 * Lock the file
2433 */
2435 if (Status != STATUS_SUCCESS)
2436 {
2437 ObDereferenceObject(Section);
2438 return Status;
2439 }
2440
2441 /* Lock the PFN lock while messing with Section Object pointers */
2442grab_segment:
2443 OldIrql = MiAcquirePfnLock();
2444 Segment = FileObject->SectionObjectPointer->DataSectionObject;
2445
2446 while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2447 {
2448 MiReleasePfnLock(OldIrql);
2450 OldIrql = MiAcquirePfnLock();
2451 Segment = FileObject->SectionObjectPointer->DataSectionObject;
2452 }
2453
2454 /*
2455 * If this file hasn't been mapped as a data file before then allocate a
2456 * section segment to describe the data file mapping
2457 */
2458 if (Segment == NULL)
2459 {
2460 /* Release the lock. ExAllocatePoolWithTag might acquire it */
2461 MiReleasePfnLock(OldIrql);
2462
2465 if (Segment == NULL)
2466 {
2467 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2468 ObDereferenceObject(Section);
2469 return STATUS_NO_MEMORY;
2470 }
2471
2472 /* We are creating it */
2473 RtlZeroMemory(Segment, sizeof(*Segment));
2475 Segment->RefCount = 1;
2476
2477 /* Acquire lock again */
2478 OldIrql = MiAcquirePfnLock();
2479
2480 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
2481 {
2482 /* Well that's bad luck. Restart it all over */
2483 MiReleasePfnLock(OldIrql);
2485 goto grab_segment;
2486 }
2487
2488 FileObject->SectionObjectPointer->DataSectionObject = Segment;
2489
2490 /* We're safe to release the lock now */
2491 MiReleasePfnLock(OldIrql);
2492
2493 Section->Segment = (PSEGMENT)Segment;
2494
2495 /* Self-referencing segment */
2496 Segment->Flags = &Segment->SegFlags;
2497 Segment->ReferenceCount = &Segment->RefCount;
2498
2499 Segment->SectionCount = 1;
2500
2502 Segment->FileObject = FileObject;
2504
2505 Segment->Image.FileOffset = 0;
2506 Segment->Protection = SectionPageProtection;
2507
2508 Segment->Image.Characteristics = 0;
2511 {
2512 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2513 }
2514 else
2515 {
2516 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2517 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2518 }
2519 Segment->Image.VirtualAddress = 0;
2521
2522 /* We're good to use it now */
2523 OldIrql = MiAcquirePfnLock();
2524 Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2525 MiReleasePfnLock(OldIrql);
2526 }
2527 else
2528 {
2529 Section->Segment = (PSEGMENT)Segment;
2530 InterlockedIncrement64(&Segment->RefCount);
2531 InterlockedIncrementUL(&Segment->SectionCount);
2532
2533 MiReleasePfnLock(OldIrql);
2534
2536
2537 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2539 {
2540 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2541 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2542 }
2543
2545 }
2546 Section->SizeOfSection = MaximumSize;
2547
2548 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2549 *SectionObject = Section;
2550 return STATUS_SUCCESS;
2551}
2552
2553/*
2554 TODO: not that great (declaring loaders statically, having to declare all of
2555 them, having to keep them extern, etc.), will fix in the future
2556*/
2558(
2559 IN CONST VOID * FileHeader,
2560 IN SIZE_T FileHeaderSize,
2561 IN PVOID File,
2562 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2564 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2565 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2566);
2567
2569(
2570 IN CONST VOID * FileHeader,
2571 IN SIZE_T FileHeaderSize,
2572 IN PVOID File,
2573 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2575 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2576 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2577);
2578
2580{
2582#ifdef __ELF
2584#endif
2585};
2586
2587static
2589NTAPI
2591{
2592 SIZE_T SizeOfSegments;
2593 PMM_SECTION_SEGMENT Segments;
2594
2595 /* TODO: check for integer overflow */
2596 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2597
2599 SizeOfSegments,
2601
2602 if(Segments)
2603 RtlZeroMemory(Segments, SizeOfSegments);
2604
2605 return Segments;
2606}
2607static
2609NTAPI
2612 IN ULONG Length,
2613 OUT PVOID * Data,
2614 OUT PVOID * AllocBase,
2615 OUT PULONG ReadSize)
2616{
2619 ULONG AdjustOffset;
2620 ULONG OffsetAdjustment;
2622 ULONG UsedSize;
2623 PVOID Buffer;
2626
2628
2629 if(Length == 0)
2630 {
2631 KeBugCheck(MEMORY_MANAGEMENT);
2632 }
2633
2634 FileOffset = *Offset;
2635
2636 /* Negative/special offset: it cannot be used in this context */
2637 if(FileOffset.u.HighPart < 0)
2638 {
2639 KeBugCheck(MEMORY_MANAGEMENT);
2640 }
2641
2642 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2643 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2644 FileOffset.u.LowPart = AdjustOffset;
2645
2646 BufferSize = Length + OffsetAdjustment;
2648
2649 /*
2650 * It's ok to use paged pool, because this is a temporary buffer only used in
2651 * the loading of executables. The assumption is that MmCreateSection is
2652 * always called at low IRQLs and that these buffers don't survive a brief
2653 * initialization phase
2654 */
2656 if (!Buffer)
2657 {
2659 }
2660
2662
2663 UsedSize = (ULONG)Iosb.Information;
2664
2665 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2666 {
2669 }
2670
2671 if(NT_SUCCESS(Status))
2672 {
2673 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2674 *AllocBase = Buffer;
2675 *ReadSize = UsedSize - OffsetAdjustment;
2676 }
2677 else
2678 {
2679 ExFreePoolWithTag(Buffer, 'rXmM');
2680 }
2681
2682 return Status;
2683}
2684
2685#ifdef NASSERT
2686# define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2687# define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2688# define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2689#else
2690static
2691VOID
2692NTAPI
2694{
2695 ULONG i;
2696
2697 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2698 {
2699 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2700 ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2701 }
2702}
2703
2704static
2705VOID
2706NTAPI
2708{
2709 ULONG i;
2710
2711 MmspAssertSegmentsSorted(ImageSectionObject);
2712
2713 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2714 {
2715 ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2716
2717 if(i > 0)
2718 {
2719 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2720 (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2721 ImageSectionObject->Segments[i - 1].Length.QuadPart));
2722 }
2723 }
2724}
2725
2726static
2727VOID
2728NTAPI
2730{
2731 ULONG i;
2732
2733 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2734 {
2735 ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2736 ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2737 }
2738}
2739#endif
2740
2741static
2742int
2743__cdecl
2745 const void * y)
2746{
2747 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2748 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2749
2750 if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2751 return 1;
2752 else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2753 return -1;
2754 else
2755 return 0;
2756}
2757
2758/*
2759 * Ensures an image section's segments are sorted in memory
2760 */
2761static
2762VOID
2763NTAPI
2765 IN ULONG Flags)
2766{
2768 {
2769 MmspAssertSegmentsSorted(ImageSectionObject);
2770 }
2771 else
2772 {
2773 qsort(ImageSectionObject->Segments,
2774 ImageSectionObject->NrSegments,
2775 sizeof(ImageSectionObject->Segments[0]),
2777 }
2778}
2779
2780
2781/*
2782 * Ensures an image section's segments don't overlap in memory and don't have
2783 * gaps and don't have a null size. We let them map to overlapping file regions,
2784 * though - that's not necessarily an error
2785 */
2786static
2787BOOLEAN
2788NTAPI
2790(
2791 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2792 IN ULONG Flags
2793)
2794{
2795 ULONG i;
2796
2798 {
2799 MmspAssertSegmentsNoOverlap(ImageSectionObject);
2800 return TRUE;
2801 }
2802
2803 ASSERT(ImageSectionObject->NrSegments >= 1);
2804
2805 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2806 {
2807 if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2808 {
2809 return FALSE;
2810 }
2811
2812 if(i > 0)
2813 {
2814 /*
2815 * TODO: relax the limitation on gaps. For example, gaps smaller than a
2816 * page could be OK (Windows seems to be OK with them), and larger gaps
2817 * could lead to image sections spanning several discontiguous regions
2818 * (NtMapViewOfSection could then refuse to map them, and they could
2819 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2820 */
2821 if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2822 ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2823 ImageSectionObject->Segments[i].Image.VirtualAddress)
2824 {
2825 return FALSE;
2826 }
2827 }
2828 }
2829
2830 return TRUE;
2831}
2832
2833/*
2834 * Merges and pads an image section's segments until they all are page-aligned
2835 * and have a size that is a multiple of the page size
2836 */
2837static
2838BOOLEAN
2839NTAPI
2841(
2842 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2843 IN ULONG Flags
2844)
2845{
2846 ULONG i;
2847 ULONG LastSegment;
2848 PMM_SECTION_SEGMENT EffectiveSegment;
2849
2851 {
2852 MmspAssertSegmentsPageAligned(ImageSectionObject);
2853 return TRUE;
2854 }
2855
2856 LastSegment = 0;
2857 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2858
2859 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2860 {
2861 /*
2862 * The first segment requires special handling
2863 */
2864 if (i == 0)
2865 {
2867 ULONG_PTR VirtualOffset;
2868
2869 VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2870
2871 /* Round down the virtual address to the nearest page */
2872 EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2873
2874 /* Round up the virtual size to the nearest page */
2875 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2876 EffectiveSegment->Image.VirtualAddress;
2877
2878 /* Adjust the raw address and size */
2879 VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2880
2881 if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2882 {
2883 return FALSE;
2884 }
2885
2886 /*
2887 * Garbage in, garbage out: unaligned base addresses make the file
2888 * offset point in curious and odd places, but that's what we were
2889 * asked for
2890 */
2891 EffectiveSegment->Image.FileOffset -= VirtualOffset;
2892 EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2893 }
2894 else
2895 {
2896 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2897 ULONG_PTR EndOfEffectiveSegment;
2898
2899 EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2900 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2901
2902 /*
2903 * The current segment begins exactly where the current effective
2904 * segment ended, therefore beginning a new effective segment
2905 */
2906 if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2907 {
2908 LastSegment ++;
2909 ASSERT(LastSegment <= i);
2910 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2911
2912 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2913
2914 if (LastSegment != i)
2915 {
2916 /*
2917 * Copy the current segment. If necessary, the effective segment
2918 * will be expanded later
2919 */
2920 *EffectiveSegment = *Segment;
2921 }
2922
2923 /*
2924 * Page-align the virtual size. We know for sure the virtual address
2925 * already is
2926 */
2927 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2928 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2929 }
2930 /*
2931 * The current segment is still part of the current effective segment:
2932 * extend the effective segment to reflect this
2933 */
2934 else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2935 {
2936 static const ULONG FlagsToProtection[16] =
2937 {
2954 };
2955
2956 unsigned ProtectionFlags;
2957
2958 /*
2959 * Extend the file size
2960 */
2961
2962 /* Unaligned segments must be contiguous within the file */
2963 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2964 EffectiveSegment->RawLength.QuadPart))
2965 {
2966 return FALSE;
2967 }
2968
2969 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2970
2971 /*
2972 * Extend the virtual size
2973 */
2974 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2975
2976 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2977 EffectiveSegment->Image.VirtualAddress;
2978
2979 /*
2980 * Merge the protection
2981 */
2982 EffectiveSegment->Protection |= Segment->Protection;
2983
2984 /* Clean up redundance */
2985 ProtectionFlags = 0;
2986
2987 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2988 ProtectionFlags |= 1 << 0;
2989
2990 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2991 ProtectionFlags |= 1 << 1;
2992
2993 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2994 ProtectionFlags |= 1 << 2;
2995
2996 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2997 ProtectionFlags |= 1 << 3;
2998
2999 ASSERT(ProtectionFlags < 16);
3000 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3001
3002 /* If a segment was required to be shared and cannot, fail */
3003 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3004 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3005 {
3006 return FALSE;
3007 }
3008 }
3009 /*
3010 * We assume no holes between segments at this point
3011 */
3012 else
3013 {
3014 KeBugCheck(MEMORY_MANAGEMENT);
3015 }
3016 }
3017 }
3018 ImageSectionObject->NrSegments = LastSegment + 1;
3019
3020 return TRUE;
3021}
3022
3025 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3026{
3028 PVOID FileHeader;
3029 PVOID FileHeaderBuffer;
3030 ULONG FileHeaderSize;
3031 ULONG Flags;
3032 ULONG OldNrSegments;
3034 ULONG i;
3035
3036 /*
3037 * Read the beginning of the file (2 pages). Should be enough to contain
3038 * all (or most) of the headers
3039 */
3040 Offset.QuadPart = 0;
3041
3043 &Offset,
3044 PAGE_SIZE * 2,
3045 &FileHeader,
3046 &FileHeaderBuffer,
3047 &FileHeaderSize);
3048
3049 if (!NT_SUCCESS(Status))
3050 return Status;
3051
3052 if (FileHeaderSize == 0)
3053 {
3054 ExFreePool(FileHeaderBuffer);
3055 return STATUS_UNSUCCESSFUL;
3056 }
3057
3058 /*
3059 * Look for a loader that can handle this executable
3060 */
3061 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3062 {
3063 Flags = 0;
3064
3065 Status = ExeFmtpLoaders[i](FileHeader,
3066 FileHeaderSize,
3067 FileObject,
3068 ImageSectionObject,
3069 &Flags,
3072
3073 if (!NT_SUCCESS(Status))
3074 {
3075 if (ImageSectionObject->Segments)
3076 {
3077 ExFreePool(ImageSectionObject->Segments);
3078 ImageSectionObject->Segments = NULL;
3079 }
3080 }
3081
3083 break;
3084 }
3085
3086 ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3087
3088 /*
3089 * No loader handled the format
3090 */
3092 {
3095 }
3096
3097 if (!NT_SUCCESS(Status))
3098 return Status;
3099
3100 ASSERT(ImageSectionObject->Segments != NULL);
3101 ASSERT(ImageSectionObject->RefCount > 0);
3102
3103 /*
3104 * Some defaults
3105 */
3106 /* FIXME? are these values platform-dependent? */
3107 if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3108 ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3109
3110 if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3111 ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3112
3113 if(ImageSectionObject->BasedAddress == NULL)
3114 {
3115 if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3116 ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3117 else
3118 ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3119 }
3120
3121 /*
3122 * And now the fun part: fixing the segments
3123 */
3124
3125 /* Sort them by virtual address */
3126 MmspSortSegments(ImageSectionObject, Flags);
3127
3128 /* Ensure they don't overlap in memory */
3129 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3131
3132 /* Ensure they are aligned */
3133 OldNrSegments = ImageSectionObject->NrSegments;
3134
3135 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3137
3138 /* Trim them if the alignment phase merged some of them */
3139 if (ImageSectionObject->NrSegments < OldNrSegments)
3140 {
3141 PMM_SECTION_SEGMENT Segments;
3142 SIZE_T SizeOfSegments;
3143
3144 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3145
3147 SizeOfSegments,
3149
3150 if (Segments == NULL)
3152
3153 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3154 ExFreePool(ImageSectionObject->Segments);
3155 ImageSectionObject->Segments = Segments;
3156 }
3157
3158 /* And finish their initialization */
3159 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3160 {
3161 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3162 ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3163 ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3164 MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3165 ImageSectionObject->Segments[i].FileObject = FileObject;
3166 }
3167
3168 ASSERT(ImageSectionObject->RefCount > 0);
3169
3170 ImageSectionObject->FileObject = FileObject;
3171
3173 return Status;
3174}
3175
3180 PLARGE_INTEGER UMaximumSize,
3184{
3185 PSECTION Section;
3187 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3188 KIRQL OldIrql;
3189
3190
3191 if (FileObject == NULL)
3193
3194 if (FileObject->SectionObjectPointer == NULL)
3195 {
3196 DPRINT1("Denying section creation due to missing cache initialization\n");
3198 }
3199
3200 /*
3201 * Create the section
3202 */
3207 NULL,
3208 sizeof(*Section),
3209 0,
3210 0,
3211 (PVOID*)(PVOID)&Section);
3212 if (!NT_SUCCESS(Status))
3213 {
3214 return Status;
3215 }
3216
3217 /*
3218 * Initialize it
3219 */
3220 RtlZeroMemory(Section, sizeof(*Section));
3221
3222 /* Mark this as a "ROS" Section */
3223 Section->u.Flags.filler = 1;
3224
3226 Section->u.Flags.File = 1;
3227 Section->u.Flags.Image = 1;
3229 Section->u.Flags.NoChange = 1;
3230
3231grab_image_section_object:
3232 OldIrql = MiAcquirePfnLock();
3233
3234 /* Wait for it to be properly created or deleted */
3235 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3236 while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3237 {
3238 MiReleasePfnLock(OldIrql);
3239
3241
3242 OldIrql = MiAcquirePfnLock();
3243 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3244 }
3245
3246 if (ImageSectionObject == NULL)
3247 {
3248 NTSTATUS StatusExeFmt;
3249
3250 /* Release the lock because ExAllocatePoolWithTag could need to acquire it */
3251 MiReleasePfnLock(OldIrql);
3252
3253 ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3254 if (ImageSectionObject == NULL)
3255 {
3256 ObDereferenceObject(Section);
3257 return STATUS_NO_MEMORY;
3258 }
3259
3260 ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3261 ImageSectionObject->RefCount = 1;
3262 ImageSectionObject->SectionCount = 1;
3263
3264 OldIrql = MiAcquirePfnLock();
3265 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
3266 {
3267 MiReleasePfnLock(OldIrql);
3268 /* Bad luck. Start over */
3269 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3270 goto grab_image_section_object;
3271 }
3272
3273 FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3274
3275 MiReleasePfnLock(OldIrql);
3276
3277 /* Purge the cache */
3278 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3279
3280 StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3281
3282 if (!NT_SUCCESS(StatusExeFmt))
3283 {
3284 /* Unset */
3285 OldIrql = MiAcquirePfnLock();
3286 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3287 MiReleasePfnLock(OldIrql);
3288
3289 if(ImageSectionObject->Segments != NULL)
3290 ExFreePool(ImageSectionObject->Segments);
3291
3292 /*
3293 * If image file is empty, then return that the file is invalid for section
3294 */
3295 Status = StatusExeFmt;
3296 if (StatusExeFmt == STATUS_END_OF_FILE)
3297 {
3299 }
3300
3301 ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3302 ObDereferenceObject(Section);
3303 return Status;
3304 }
3305
3306 Section->Segment = (PSEGMENT)ImageSectionObject;
3307 ASSERT(ImageSectionObject->Segments);
3308 ASSERT(ImageSectionObject->RefCount > 0);
3309
3310 /*
3311 * Lock the file
3312 */
3314 if (!NT_SUCCESS(Status))
3315 {
3316 /* Unset */
3317 OldIrql = MiAcquirePfnLock();
3318 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3319 MiReleasePfnLock(OldIrql);
3320
3321 ExFreePool(ImageSectionObject->Segments);
3322 ExFreePool(ImageSectionObject);
3323 ObDereferenceObject(Section);
3324 return Status;
3325 }
3326
3327 OldIrql = MiAcquirePfnLock();
3328 ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3329
3330 /* Take a ref on the file on behalf of the newly created structure */
3332
3333 MiReleasePfnLock(OldIrql);
3334
3335 Status = StatusExeFmt;
3336 }
3337 else
3338 {
3339 /* If FS driver called for delete, tell them it's not possible anymore. */
3340 ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
3341
3342 /* Take one ref */
3343 InterlockedIncrement64(&ImageSectionObject->RefCount);
3344 ImageSectionObject->SectionCount++;
3345
3346 MiReleasePfnLock(OldIrql);
3347
3348 Section->Segment = (PSEGMENT)ImageSectionObject;
3349
3351 }
3352 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3353 *SectionObject = Section;
3354 ASSERT(ImageSectionObject->RefCount > 0);
3355
3356 return Status;
3357}
3358
3359
3360
3361static NTSTATUS
3364 BOOLEAN AsImage,
3368 ULONG Protect,
3369 LONGLONG ViewOffset,
3371{
3372 PMEMORY_AREA MArea;
3374 ULONG Granularity;
3375
3376 ASSERT(ViewSize != 0);
3377
3378 if (Segment->WriteCopy)
3379 {
3380 /* We have to do this because the not present fault
3381 * and access fault handlers depend on the protection
3382 * that should be granted AFTER the COW fault takes
3383 * place to be in Region->Protect. The not present fault
3384 * handler changes this to the correct protection for COW when
3385 * mapping the pages into the process's address space. If a COW
3386 * fault takes place, the access fault handler sets the page protection
3387 * to these values for the newly copied pages
3388 */
3389 if (Protect == PAGE_WRITECOPY)
3391 else if (Protect == PAGE_EXECUTE_WRITECOPY)
3393 }
3394
3395 if (*BaseAddress == NULL)
3396 Granularity = MM_ALLOCATION_GRANULARITY;
3397 else
3398 Granularity = PAGE_SIZE;
3399
3400#ifdef NEWCC
3401 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3402 {
3404 FileOffset.QuadPart = ViewOffset;
3405 ObReferenceObject(Section);
3407 }
3408#endif
3412 ViewSize,
3413 Protect,
3414 &MArea,
3416 Granularity);
3417 if (!NT_SUCCESS(Status))
3418 {
3419 DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3420 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3421 return Status;
3422 }
3423
3424 InterlockedIncrement64(Segment->ReferenceCount);
3425
3426 MArea->SectionData.Segment = Segment;
3427 MArea->SectionData.ViewOffset = ViewOffset;
3428 if (AsImage)
3429 {
3431 }
3432
3433 MmInitializeRegion(&MArea->SectionData.RegionListHead,
3434 ViewSize, 0, Protect);
3435
3436 return STATUS_SUCCESS;
3437}
3438
3439
3440static VOID
3442 PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3443{
3446 SWAPENTRY SavedSwapEntry;
3450
3453
3455
3457 MemoryArea->SectionData.ViewOffset;
3458
3459 Segment = MemoryArea->SectionData.Segment;
3460
3462 while (Entry && MM_IS_WAIT_PTE(Entry))
3463 {
3466
3468
3472 }
3473
3474 /*
3475 * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3476 */
3477 if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3478 {
3479 if (Page == PFN_FROM_SSE(Entry) && Dirty)
3480 {
3481 ASSERT(SwapEntry == 0);
3482 }
3483 }
3484
3485 if (SwapEntry != 0)
3486 {
3487 /*
3488 * Sanity check
3489 */
3490 MmFreeSwapPage(SwapEntry);
3491 }
3492 else if (Page != 0)
3493 {
3494 if (IS_SWAP_FROM_SSE(Entry) ||
3496 {
3497 ASSERT(Process != NULL);
3498
3499 /*
3500 * Just dereference private pages
3501 */
3502 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3503 if (SavedSwapEntry != 0)
3504 {
3505 MmFreeSwapPage(SavedSwapEntry);
3507 }
3510 }
3511 else
3512 {
3513 if (Process)
3514 {
3516 }
3517
3518 /* We don't dirtify for System Space Maps. We let Cc manage that */
3520 }
3521 }
3522}
3523
3524static NTSTATUS
3527{
3531 PLIST_ENTRY CurrentEntry;
3532 PMM_REGION CurrentRegion;
3533 PLIST_ENTRY RegionListHead;
3534
3536 BaseAddress);
3537 if (MemoryArea == NULL)
3538 {
3539 return STATUS_UNSUCCESSFUL;
3540 }
3541
3542 Segment = MemoryArea->SectionData.Segment;
3543
3544#ifdef NEWCC
3545 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3546 {
3550
3551 return Status;
3552 }
3553#endif
3554
3556
3558
3559 RegionListHead = &MemoryArea->SectionData.RegionListHead;
3560 while (!IsListEmpty(RegionListHead))
3561 {
3562 CurrentEntry = RemoveHeadList(RegionListHead);
3563 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3564 ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3565 }
3566
3567 if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3568 {
3570 MemoryArea,
3571 NULL,
3572 NULL);
3573 }
3574 else
3575 {
3577 MemoryArea,
3579 AddressSpace);
3580 }
3582 MmDereferenceSegment(Segment);
3583 return Status;
3584}
3585
3586/* This functions must be called with a locked address space */
3588NTAPI
3591 IN BOOLEAN SkipDebuggerNotify)
3592{
3596 PVOID ImageBaseAddress = 0;
3597
3598 DPRINT("Opening memory area Process %p BaseAddress %p\n",
3600
3601 ASSERT(Process);
3602
3603 AddressSpace = &Process->Vm;
3604
3606 BaseAddress);
3607 if (MemoryArea == NULL ||
3608#ifdef NEWCC
3609 ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3610#else
3612#endif
3614
3615 {
3617
3618 DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3620 }
3621
3623 {
3624 ULONG i;
3625 ULONG NrSegments;
3626 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3627 PMM_SECTION_SEGMENT SectionSegments;
3629
3630 Segment = MemoryArea->SectionData.Segment;
3631 ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3632 SectionSegments = ImageSectionObject->Segments;
3633 NrSegments = ImageSectionObject->NrSegments;
3634
3636
3637 /* Search for the current segment within the section segments
3638 * and calculate the image base address */
3639 for (i = 0; i < NrSegments; i++)
3640 {
3641 if (Segment == &SectionSegments[i])
3642 {
3643 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3644 break;
3645 }
3646 }
3647 if (i >= NrSegments)
3648 {
3649 KeBugCheck(MEMORY_MANAGEMENT);
3650 }
3651
3652 for (i = 0; i < NrSegments; i++)
3653 {
3654 PVOID SBaseAddress = (PVOID)
3655 ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3656
3657 Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3658 if (!NT_SUCCESS(Status))
3659 {
3660 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3661 SBaseAddress, Process, Status);
3663 }
3664 }
3665 DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer);
3666 InterlockedDecrement(&ImageSectionObject->MapCount);
3667 }
3668 else
3669 {
3671 PMMVAD Vad = &MemoryArea->VadNode;
3672 PCONTROL_AREA ControlArea = Vad->ControlArea;
3675 LARGE_INTEGER ViewOffset;
3676 ViewOffset.QuadPart = MemoryArea->SectionData.ViewOffset;
3677
3678 InterlockedIncrement64(Segment->ReferenceCount);
3679
3680 ViewSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
3681
3683 if (!NT_SUCCESS(Status))
3684 {
3685 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3688 }
3689
3690 /* These might be deleted now */
3691 Vad = NULL;
3692 MemoryArea = NULL;
3693
3695 {
3696 /* Don't bother */
3697 MmDereferenceSegment(Segment);
3698 return STATUS_SUCCESS;
3699 }
3701
3702 FileObject = Segment->FileObject;
3704
3705 /* Don't bother for auto-delete closed file. */
3707 {
3709 MmDereferenceSegment(Segment);
3710 return STATUS_SUCCESS;
3711 }
3712
3713 /*
3714 * Flush only when last mapping is deleted.
3715 * FIXME: Why ControlArea == NULL? Or rather: is ControlArea ever not NULL here?
3716 */
3717 if (ControlArea == NULL || ControlArea->NumberOfMappedViews == 1)
3718 {
3719 while (ViewSize > 0)
3720 {
3721 ULONG FlushSize = min(ViewSize, PAGE_ROUND_DOWN(MAXULONG));
3722 MmFlushSegment(FileObject->SectionObjectPointer,
3723 &ViewOffset,
3724 FlushSize,
3725 NULL);
3726 ViewSize -= FlushSize;
3727 ViewOffset.QuadPart += FlushSize;
3728 }
3729 }
3730
3732 MmDereferenceSegment(Segment);
3733 }
3734
3735 /* Notify debugger */
3736 if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3737
3738 return STATUS_SUCCESS;
3739}
3740
3741
3742
3743
3767NTAPI
3769 _In_ HANDLE SectionHandle,
3770 _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3771 _Out_ PVOID SectionInformation,
3772 _In_ SIZE_T SectionInformationLength,
3774{
3775 PSECTION Section;
3778 PAGED_CODE();
3779
3781 if (PreviousMode != KernelMode)
3782 {
3783 _SEH2_TRY
3784 {
3785 ProbeForWrite(SectionInformation,
3786 SectionInformationLength,
3787 __alignof(ULONG));
3788 if (ResultLength != NULL)
3789 {
3791 sizeof(*ResultLength),
3792 __alignof(SIZE_T));
3793 }
3794 }
3796 {
3798 }
3799 _SEH2_END;
3800 }
3801
3802 if (SectionInformationClass == SectionBasicInformation)
3803 {
3804 if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3805 {
3807 }
3808 }
3809 else if (SectionInformationClass == SectionImageInformation)
3810 {
3811 if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3812 {
3814 }
3815 }
3816 else
3817 {
3819 }
3820
3821 Status = ObReferenceObjectByHandle(SectionHandle,
3825 (PVOID*)(PVOID)&Section,
3826 NULL);
3827 if (!NT_SUCCESS(Status))
3828 {
3829 DPRINT1("Failed to reference section: 0x%lx\n", Status);
3830 return Status;
3831 }
3832
3833 switch(SectionInformationClass)
3834 {
3836 {
3838
3839 Sbi.Size = Section->SizeOfSection;
3840 Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3841
3842 Sbi.Attributes = 0;
3843 if (Section->u.Flags.File)
3844 Sbi.Attributes |= SEC_FILE;
3845 if (Section->u.Flags.Image)
3846 Sbi.Attributes |= SEC_IMAGE;
3847
3848 /* Those are not set *************
3849 if (Section->u.Flags.Commit)
3850 Sbi.Attributes |= SEC_COMMIT;
3851 if (Section->u.Flags.Reserve)
3852 Sbi.Attributes |= SEC_RESERVE;
3853 **********************************/
3854
3855 if (Section->u.Flags.Image)
3856 {
3857 if (MiIsRosSectionObject(Section))
3858 {
3859 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3860 Sbi.BaseAddress = 0;
3861 Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3862 }
3863 else
3864 {
3865 /* Not supported yet */
3866 ASSERT(FALSE);
3867 }
3868 }
3869 else if (MiIsRosSectionObject(Section))
3870 {
3871 Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3872 Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3873 }
3874 else
3875 {
3876 DPRINT1("Unimplemented code path\n");
3877 }
3878
3879 _SEH2_TRY
3880 {
3881 *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3882 if (ResultLength != NULL)
3883 {
3884 *ResultLength = sizeof(Sbi);
3885 }
3886 }
3888 {
3890 }
3891 _SEH2_END;
3892 break;
3893 }
3895 {
3896 if (!Section->u.Flags.Image)
3897 {
3899 }
3900 else if (MiIsRosSectionObject(Section))
3901 {
3902 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3903
3904 _SEH2_TRY
3905 {
3907 *Sii = ImageSectionObject->ImageInformation;
3908 if (ResultLength != NULL)
3909 {
3910 *ResultLength = sizeof(*Sii);
3911 }
3912 }
3914 {
3916 }
3917 _SEH2_END;
3918 }
3919 else
3920 {
3921 _SEH2_TRY
3922 {
3924 *Sii = *Section->Segment->u2.ImageInformation;
3925 if (ResultLength != NULL)
3926 *ResultLength = sizeof(*Sii);
3927 }
3929 {
3931 }
3932 _SEH2_END;
3933 }
3934 break;
3935 }
3936 default:
3937 DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3939 }
3940
3941 ObDereferenceObject(Section);
3942
3943 return Status;
3944}
3945
3946/**********************************************************************
3947 * NAME EXPORTED
3948 * MmMapViewOfSection
3949 *
3950 * DESCRIPTION
3951 * Maps a view of a section into the virtual address space of a
3952 * process.
3953 *
3954 * ARGUMENTS
3955 * Section
3956 * Pointer to the section object.
3957 *
3958 * ProcessHandle
3959 * Pointer to the process.
3960 *
3961 * BaseAddress
3962 * Desired base address (or NULL) on entry;
3963 * Actual base address of the view on exit.
3964 *
3965 * ZeroBits
3966 * Number of high order address bits that must be zero.
3967 *
3968 * CommitSize
3969 * Size in bytes of the initially committed section of
3970 * the view.
3971 *
3972 * SectionOffset
3973 * Offset in bytes from the beginning of the section
3974 * to the beginning of the view.
3975 *
3976 * ViewSize
3977 * Desired length of map (or zero to map all) on entry
3978 * Actual length mapped on exit.
3979 *
3980 * InheritDisposition
3981 * Specified how the view is to be shared with
3982 * child processes.
3983 *
3984 * AllocationType
3985 * Type of allocation for the pages.
3986 *
3987 * Protect
3988 * Protection for the committed region of the view.
3989 *
3990 * RETURN VALUE
3991 * Status.
3992 *
3993 * @implemented
3994 */
4006{
4007 PSECTION Section;
4010 BOOLEAN NotAtBase = FALSE;
4011 BOOLEAN IsAttached = FALSE;
4013
4015 {
4016 DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
4018 Process,
4020 ZeroBits,
4021 CommitSize,
4023 ViewSize,
4026 Protect);
4027 }
4028
4029 ASSERT(Process);
4030
4032 {
4034 }
4035
4037 {
4039 IsAttached = TRUE;
4040 }
4041
4042 /* FIXME: We should keep this, but it would break code checking equality */
4043 Protect &= ~PAGE_NOCACHE;
4044
4045 Section = SectionObject;
4046 AddressSpace = &Process->Vm;
4047
4048 if (Section->u.Flags.NoChange)
4050
4052
4053 if (Section->u.Flags.Image)
4054 {
4055 ULONG i;
4056 ULONG NrSegments;
4057 ULONG_PTR ImageBase;
4058 SIZE_T ImageSize;
4059 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4060 PMM_SECTION_SEGMENT SectionSegments;
4061
4062 ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
4063 SectionSegments = ImageSectionObject->Segments;
4064 NrSegments = ImageSectionObject->NrSegments;
4065
4066 ASSERT(ImageSectionObject->RefCount > 0);
4067
4068 ImageBase = (ULONG_PTR)*BaseAddress;
4069 if (ImageBase == 0)
4070 {
4071 ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
4072 }
4073
4074 ImageSize = 0;
4075 for (i = 0; i < NrSegments; i++)
4076 {
4077 ULONG_PTR MaxExtent;
4078 MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
4079 SectionSegments[i].Length.QuadPart);
4080 ImageSize = max(ImageSize, MaxExtent);
4081 }
4082
4083 ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
4084
4085 /* Check for an illegal base address */
4086 if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
4087 ((ImageBase + ImageSize) < ImageSize))
4088 {
4089 ASSERT(*BaseAddress == NULL);
4090 ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
4092 NotAtBase = TRUE;
4093 }
4094 else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
4095 {
4096 ASSERT(*BaseAddress == NULL);
4097 ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4098 NotAtBase = TRUE;
4099 }
4100
4101 /* Check there is enough space to map the section at that point. */
4103 PAGE_ROUND_UP(ImageSize)) != NULL)
4104 {
4105 /* Fail if the user requested a fixed base address. */
4106 if ((*BaseAddress) != NULL)
4107 {
4109 goto Exit;
4110 }
4111 /* Otherwise find a gap to map the image. */
4113 if (ImageBase == 0)
4114 {
4116 goto Exit;
4117 }
4118 /* Remember that we loaded image at a different base address */
4119 NotAtBase = TRUE;
4120 }
4121
4122 for (i = 0; i < NrSegments; i++)
4123 {
4124 PVOID SBaseAddress = (PVOID)
4125 ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4126 MmLockSectionSegment(&SectionSegments[i]);
4128 TRUE,
4129 &SectionSegments[i],
4130 &SBaseAddress,
4131 SectionSegments[i].Length.QuadPart,
4132 SectionSegments[i].Protection,
4133 0,
4134 0);
4135 MmUnlockSectionSegment(&SectionSegments[i]);
4136 if (!NT_SUCCESS(Status))
4137 {
4138 /* roll-back */
4139 while (i--)
4140 {
4141 SBaseAddress = ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4142 MmLockSectionSegment(&SectionSegments[i]);
4143 MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4144 MmUnlockSectionSegment(&SectionSegments[i]);
4145 }
4146
4147 goto Exit;
4148 }
4149 }
4150
4151 *BaseAddress = (PVOID)ImageBase;
4152 *ViewSize = ImageSize;
4153
4154 DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4155
4156 /* One more map */
4157 InterlockedIncrement(&ImageSectionObject->MapCount);
4158 }
4159 else
4160 {
4162 LONGLONG ViewOffset;
4163
4164 ASSERT(Segment->RefCount > 0);
4165
4166 /* check for write access */
4169 {
4171 goto Exit;
4172 }
4173 /* check for read access */
4176 {
4178 goto Exit;
4179 }
4180 /* check for execute access */
4183 {
4185 goto Exit;
4186 }
4187
4188 if (SectionOffset == NULL)
4189 {
4190 ViewOffset = 0;
4191 }
4192 else
4193 {
4194 ViewOffset = SectionOffset->QuadPart;
4195 }
4196
4197 if ((ViewOffset % PAGE_SIZE) != 0)
4198 {
4200 goto Exit;
4201 }
4202
4203 if ((*ViewSize) == 0)
4204 {
4205 (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4206 }
4207 else if ((ExGetPreviousMode() == UserMode) &&
4208 (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4209 (!Section->u.Flags.Reserve))
4210 {
4211 /* Dubious */
4212 (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4213 }
4214
4216
4219 FALSE,
4220 Segment,
4222 *ViewSize,
4223 Protect,
4224 ViewOffset,
4227 if (!NT_SUCCESS(Status))
4228 {
4229 goto Exit;
4230 }
4231 }
4232
4233 if (NotAtBase)
4235 else
4237
4238Exit:
4239
4241
4242 if (IsAttached)
4243 {
4245 }
4246
4247 return Status;
4248}
4249
4250/*
4251 * @unimplemented
4252 */
4253BOOLEAN
4254NTAPI
4258{
4259 BOOLEAN Ret;
4261
4262 /* Check whether an ImageSectionObject exists */
4263 if (SectionObjectPointer->ImageSectionObject != NULL)
4264 {
4265 DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4266 return FALSE;
4267 }
4268
4270 if (!Segment)
4271 {
4272 /* There is no data section. It's fine to do anything. */
4273 return TRUE;
4274 }
4275
4277 if ((Segment->SectionCount == 0) ||
4278 ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4279 {
4280 /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4281 Ret = TRUE;
4282 }
4283 else if (NewFileSize != NULL)
4284 {
4285 /* We can't shrink, but we can extend */
4286 Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4287#if DBG
4288 if (!Ret)
4289 {
4290 DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4291 }
4292#endif
4293 }
4294 else
4295 {
4296 DPRINT1("ERROR: File can't be truncated because it has references held to its data section\n");
4297 Ret = FALSE;
4298 }
4299
4301 MmDereferenceSegment(Segment);
4302
4303 DPRINT("FIXME: didn't check for outstanding write probes\n");
4304
4305 return Ret;
4306}
4307
4308static
4309BOOLEAN
4311{
4313
4315
4316 /* Loop over all entries */
4317 for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4318 PageTable != NULL;
4320 {
4321 for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4322 {
4323 ULONG_PTR Entry = PageTable->PageEntries[i];
4325
4326 if (!Entry)
4327 continue;
4328
4330 {
4331 /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4333 return FALSE;
4334 }
4335
4336 /* Regular entry */
4339
4340 /* Properly remove using the used API */
4341 Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4344 }
4345 }
4346
4348
4349 return TRUE;
4350}
4351
4352/*
4353 * @implemented
4354 */