2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/pe.c
5 * PURPOSE: Loader for PE executables
7 * PROGRAMMERS: KJK::Hyperion <hackbunny@reactos.com>
10 /* INCLUDES *****************************************************************/
17 #include <reactos/exeformat.h>
20 #define MAXULONG ((ULONG)(~1))
23 static ULONG SectionCharacteristicsToProtect
[16] =
25 PAGE_NOACCESS
, /* 0 = NONE */
26 PAGE_NOACCESS
, /* 1 = SHARED */
27 PAGE_EXECUTE
, /* 2 = EXECUTABLE */
28 PAGE_EXECUTE
, /* 3 = EXECUTABLE, SHARED */
29 PAGE_READONLY
, /* 4 = READABLE */
30 PAGE_READONLY
, /* 5 = READABLE, SHARED */
31 PAGE_EXECUTE_READ
, /* 6 = READABLE, EXECUTABLE */
32 PAGE_EXECUTE_READ
, /* 7 = READABLE, EXECUTABLE, SHARED */
34 * FIXME? do we really need the WriteCopy field in segments? can't we use
35 * PAGE_WRITECOPY here?
37 PAGE_READWRITE
, /* 8 = WRITABLE */
38 PAGE_READWRITE
, /* 9 = WRITABLE, SHARED */
39 PAGE_EXECUTE_READWRITE
, /* 10 = WRITABLE, EXECUTABLE */
40 PAGE_EXECUTE_READWRITE
, /* 11 = WRITABLE, EXECUTABLE, SHARED */
41 PAGE_READWRITE
, /* 12 = WRITABLE, READABLE */
42 PAGE_READWRITE
, /* 13 = WRITABLE, READABLE, SHARED */
43 PAGE_EXECUTE_READWRITE
, /* 14 = WRITABLE, READABLE, EXECUTABLE */
44 PAGE_EXECUTE_READWRITE
, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
47 /* TODO: Intsafe should be made into a library, as it's generally useful */
48 static __inline BOOLEAN
Intsafe_CanAddULongPtr(IN ULONG_PTR Addend1
, IN ULONG_PTR Addend2
)
50 return Addend1
<= (MAXULONG_PTR
- Addend2
);
54 #define MAXLONGLONG ((LONGLONG)((~((ULONGLONG)0)) >> 1))
57 static __inline BOOLEAN
Intsafe_CanAddLong64(IN LONG64 Addend1
, IN LONG64 Addend2
)
59 return Addend1
<= (MAXLONGLONG
- Addend2
);
62 static __inline BOOLEAN
Intsafe_CanAddULong32(IN ULONG Addend1
, IN ULONG Addend2
)
64 return Addend1
<= (MAXULONG
- Addend2
);
67 static __inline BOOLEAN
Intsafe_AddULong32(OUT PULONG Result
, IN ULONG Addend1
, IN ULONG Addend2
)
69 if(!Intsafe_CanAddULong32(Addend1
, Addend2
))
72 *Result
= Addend1
+ Addend2
;
76 static __inline BOOLEAN
Intsafe_CanMulULong32(IN ULONG Factor1
, IN ULONG Factor2
)
78 return Factor1
<= (MAXULONG
/ Factor2
);
81 static __inline BOOLEAN
Intsafe_CanOffsetPointer(IN CONST VOID
* Pointer
, IN SIZE_T Offset
)
83 /* FIXME: (PVOID)MAXULONG_PTR isn't necessarily a valid address */
84 return Intsafe_CanAddULongPtr((ULONG_PTR
)Pointer
, Offset
);
87 /* TODO: these are standard DDK/PSDK macros */
88 #ifndef RTL_FIELD_SIZE
89 #define RTL_FIELD_SIZE(TYPE_, FIELD_) (sizeof(((TYPE_ *)0)->FIELD_))
92 #ifndef RTL_SIZEOF_THROUGH_FIELD
93 #define RTL_SIZEOF_THROUGH_FIELD(TYPE_, FIELD_) \
94 (FIELD_OFFSET(TYPE_, FIELD_) + RTL_FIELD_SIZE(TYPE_, FIELD_))
97 #ifndef RTL_CONTAINS_FIELD
98 #define RTL_CONTAINS_FIELD(P_, SIZE_, FIELD_) \
99 ((ULONG_PTR)(P_) + (ULONG_PTR)(SIZE_) > (ULONG_PTR)&((P_)->FIELD_) + sizeof((P_)->FIELD_))
102 static __inline BOOLEAN
IsPowerOf2(IN ULONG Number
)
106 return (Number
& (Number
- 1)) == 0;
109 static __inline ULONG
ModPow2(IN ULONG Address
, IN ULONG Alignment
)
111 ASSERT(IsPowerOf2(Alignment
));
112 return Address
& (Alignment
- 1);
115 static __inline BOOLEAN
IsAligned(IN ULONG Address
, IN ULONG Alignment
)
117 return ModPow2(Address
, Alignment
) == 0;
120 static __inline BOOLEAN
AlignUp(OUT PULONG AlignedAddress
, IN ULONG Address
, IN ULONG Alignment
)
122 ULONG nExcess
= ModPow2(Address
, Alignment
);
126 *AlignedAddress
= Address
;
130 return Intsafe_AddULong32(AlignedAddress
, Address
, Alignment
- nExcess
);
133 #define PEFMT_FIELDS_EQUAL(TYPE1_, TYPE2_, FIELD_) \
135 (FIELD_OFFSET(TYPE1_, FIELD_) == FIELD_OFFSET(TYPE2_, FIELD_)) && \
136 (RTL_FIELD_SIZE(TYPE1_, FIELD_) == RTL_FIELD_SIZE(TYPE2_, FIELD_)) \
169 // FIXME: All this whitespace is "padding" so the C_ASSERTs aren't on the same lines as asserts in other headers.
170 // This is necessary because of the way we define C_ASSERT in a gcc compatible way.
171 // This can be removed once we upgrade to gcc 4.3.x or later (which implements __COUNTER__).
198 // PeFmtCreateSection depends on the following:
200 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE
>= sizeof(IMAGE_DOS_HEADER
));
201 C_ASSERT(sizeof(IMAGE_NT_HEADERS32
) <= sizeof(IMAGE_NT_HEADERS64
));
203 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32
) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64
));
204 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32
, FileHeader
) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64
, FileHeader
));
205 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32
, OptionalHeader
) == FIELD_OFFSET(IMAGE_NT_HEADERS64
, OptionalHeader
));
207 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, Magic
));
208 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, SectionAlignment
));
209 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, FileAlignment
));
210 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, Subsystem
));
211 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, MinorSubsystemVersion
));
212 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, MajorSubsystemVersion
));
213 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, AddressOfEntryPoint
));
214 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, SizeOfCode
));
215 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, SizeOfHeaders
));
219 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
220 File Format Specification", revision 6.0 (February 1999)
222 NTSTATUS NTAPI
PeFmtCreateSection(IN CONST VOID
* FileHeader
,
223 IN SIZE_T FileHeaderSize
,
225 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject
,
227 IN PEXEFMT_CB_READ_FILE ReadFileCb
,
228 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
)
231 ULONG cbFileHeaderOffsetSize
= 0;
232 ULONG cbSectionHeadersOffset
= 0;
233 ULONG cbSectionHeadersSize
;
234 ULONG cbSectionHeadersOffsetSize
= 0;
235 ULONG cbOptHeaderSize
;
236 ULONG cbHeadersSize
= 0;
237 ULONG nSectionAlignment
;
238 ULONG nFileAlignment
;
239 const IMAGE_DOS_HEADER
* pidhDosHeader
;
240 const IMAGE_NT_HEADERS32
* pinhNtHeader
;
241 const IMAGE_OPTIONAL_HEADER32
* piohOptHeader
;
242 const IMAGE_SECTION_HEADER
* pishSectionHeaders
;
243 PMM_SECTION_SEGMENT pssSegments
;
244 LARGE_INTEGER lnOffset
;
246 ULONG nPrevVirtualEndOfSegment
= 0;
247 ULONG nFileSizeOfHeaders
= 0;
251 ASSERT(FileHeaderSize
> 0);
253 ASSERT(ImageSectionObject
);
255 ASSERT(AllocateSegmentsCb
);
257 ASSERT(Intsafe_CanOffsetPointer(FileHeader
, FileHeaderSize
));
259 ASSERT(((UINT_PTR
)FileHeader
% TYPE_ALIGNMENT(IMAGE_DOS_HEADER
)) == 0);
261 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
264 pidhDosHeader
= FileHeader
;
267 nStatus
= STATUS_ROS_EXEFMT_UNKNOWN_FORMAT
;
269 /* image too small to be an MZ executable */
270 if(FileHeaderSize
< sizeof(IMAGE_DOS_HEADER
))
271 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize
));
273 /* no MZ signature */
274 if(pidhDosHeader
->e_magic
!= IMAGE_DOS_SIGNATURE
)
275 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader
->e_magic
));
277 /* not a Windows executable */
278 if(pidhDosHeader
->e_lfanew
<= 0)
279 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader
->e_lfanew
));
282 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
284 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize
, pidhDosHeader
->e_lfanew
, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32
, FileHeader
)))
285 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader
->e_lfanew
));
287 if(FileHeaderSize
< cbFileHeaderOffsetSize
)
292 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
293 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
295 ASSERT(Intsafe_CanOffsetPointer(FileHeader
, pidhDosHeader
->e_lfanew
));
296 pinhNtHeader
= (PVOID
)((UINT_PTR
)FileHeader
+ pidhDosHeader
->e_lfanew
);
300 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
301 * need to read the header from the file
303 if(FileHeaderSize
< cbFileHeaderOffsetSize
||
304 (UINT_PTR
)pinhNtHeader
% TYPE_ALIGNMENT(IMAGE_NT_HEADERS32
) != 0)
306 ULONG cbNtHeaderSize
;
310 l_ReadHeaderFromFile
:
312 lnOffset
.QuadPart
= pidhDosHeader
->e_lfanew
;
314 /* read the header from the file */
315 nStatus
= ReadFileCb(File
, &lnOffset
, sizeof(IMAGE_NT_HEADERS64
), &pData
, &pBuffer
, &cbReadSize
);
317 if(!NT_SUCCESS(nStatus
))
318 DIE(("ReadFile failed, status %08X\n", nStatus
));
322 ASSERT(cbReadSize
> 0);
324 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
326 /* the buffer doesn't contain the file header */
327 if(cbReadSize
< RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32
, FileHeader
))
328 DIE(("The file doesn't contain the PE file header\n"));
330 pinhNtHeader
= pData
;
332 /* object still not aligned: copy it to the beginning of the buffer */
333 if((UINT_PTR
)pinhNtHeader
% TYPE_ALIGNMENT(IMAGE_NT_HEADERS32
) != 0)
335 ASSERT((UINT_PTR
)pBuffer
% TYPE_ALIGNMENT(IMAGE_NT_HEADERS32
) == 0);
336 RtlMoveMemory(pBuffer
, pData
, cbReadSize
);
337 pinhNtHeader
= pBuffer
;
340 /* invalid NT header */
341 nStatus
= STATUS_INVALID_IMAGE_PROTECT
;
343 if(pinhNtHeader
->Signature
!= IMAGE_NT_SIGNATURE
)
344 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader
->Signature
));
346 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
348 if(!Intsafe_AddULong32(&cbNtHeaderSize
, pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
, FIELD_OFFSET(IMAGE_NT_HEADERS32
, OptionalHeader
)))
349 DIE(("The full NT header is too large\n"));
351 /* the buffer doesn't contain the whole NT header */
352 if(cbReadSize
< cbNtHeaderSize
)
353 DIE(("The file doesn't contain the full NT header\n"));
357 ULONG cbOptHeaderOffsetSize
= 0;
359 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
361 /* don't trust an invalid NT header */
362 if(pinhNtHeader
->Signature
!= IMAGE_NT_SIGNATURE
)
363 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader
->Signature
));
365 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize
, pidhDosHeader
->e_lfanew
, FIELD_OFFSET(IMAGE_NT_HEADERS32
, OptionalHeader
)))
366 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader
->e_lfanew
));
368 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize
, cbOptHeaderOffsetSize
, pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
))
369 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
));
371 /* the buffer doesn't contain the whole NT header: read it from the file */
372 if(cbOptHeaderOffsetSize
> FileHeaderSize
)
373 goto l_ReadHeaderFromFile
;
376 /* read information from the NT header */
377 piohOptHeader
= &pinhNtHeader
->OptionalHeader
;
378 cbOptHeaderSize
= pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
;
380 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
382 if(!RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, Magic
))
383 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize
));
385 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
387 switch(piohOptHeader
->Magic
)
389 case IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
390 case IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
394 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader
->Magic
));
397 if (RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SectionAlignment
) &&
398 RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, FileAlignment
))
400 /* See [1], section 3.4.2 */
401 if(piohOptHeader
->SectionAlignment
< PAGE_SIZE
)
403 if(piohOptHeader
->FileAlignment
!= piohOptHeader
->SectionAlignment
)
404 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
406 else if(piohOptHeader
->SectionAlignment
< piohOptHeader
->FileAlignment
)
407 DIE(("The section alignment is smaller than the file alignment\n"));
409 nSectionAlignment
= piohOptHeader
->SectionAlignment
;
410 nFileAlignment
= piohOptHeader
->FileAlignment
;
412 if(!IsPowerOf2(nSectionAlignment
) || !IsPowerOf2(nFileAlignment
))
413 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment
, nFileAlignment
));
417 nSectionAlignment
= PAGE_SIZE
;
418 nFileAlignment
= PAGE_SIZE
;
421 ASSERT(IsPowerOf2(nSectionAlignment
));
422 ASSERT(IsPowerOf2(nFileAlignment
));
424 switch(piohOptHeader
->Magic
)
427 case IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
429 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, ImageBase
))
430 ImageSectionObject
->ImageBase
= piohOptHeader
->ImageBase
;
432 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfImage
))
433 ImageSectionObject
->ImageSize
= piohOptHeader
->SizeOfImage
;
435 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfStackReserve
))
436 ImageSectionObject
->StackReserve
= piohOptHeader
->SizeOfStackReserve
;
438 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfStackCommit
))
439 ImageSectionObject
->StackCommit
= piohOptHeader
->SizeOfStackCommit
;
445 case IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
447 const IMAGE_OPTIONAL_HEADER64
* pioh64OptHeader
;
449 pioh64OptHeader
= (const IMAGE_OPTIONAL_HEADER64
*)piohOptHeader
;
451 if(RTL_CONTAINS_FIELD(pioh64OptHeader
, cbOptHeaderSize
, ImageBase
))
453 if(pioh64OptHeader
->ImageBase
> MAXULONG_PTR
)
454 DIE(("ImageBase exceeds the address space\n"));
456 ImageSectionObject
->ImageBase
= pioh64OptHeader
->ImageBase
;
459 if(RTL_CONTAINS_FIELD(pioh64OptHeader
, cbOptHeaderSize
, SizeOfImage
))
461 if(pioh64OptHeader
->SizeOfImage
> MAXULONG_PTR
)
462 DIE(("SizeOfImage exceeds the address space\n"));
464 ImageSectionObject
->ImageSize
= pioh64OptHeader
->SizeOfImage
;
467 if(RTL_CONTAINS_FIELD(pioh64OptHeader
, cbOptHeaderSize
, SizeOfStackReserve
))
469 if(pioh64OptHeader
->SizeOfStackReserve
> MAXULONG_PTR
)
470 DIE(("SizeOfStackReserve exceeds the address space\n"));
472 ImageSectionObject
->StackReserve
= pioh64OptHeader
->SizeOfStackReserve
;
475 if(RTL_CONTAINS_FIELD(pioh64OptHeader
, cbOptHeaderSize
, SizeOfStackCommit
))
477 if(pioh64OptHeader
->SizeOfStackCommit
> MAXULONG_PTR
)
478 DIE(("SizeOfStackCommit exceeds the address space\n"));
480 ImageSectionObject
->StackCommit
= pioh64OptHeader
->SizeOfStackCommit
;
487 /* [1], section 3.4.2 */
488 if((ULONG_PTR
)ImageSectionObject
->ImageBase
% 0x10000)
489 DIE(("ImageBase is not aligned on a 64KB boundary"));
491 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, Subsystem
))
493 ImageSectionObject
->Subsystem
= piohOptHeader
->Subsystem
;
495 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, MinorSubsystemVersion
) &&
496 RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, MajorSubsystemVersion
))
498 ImageSectionObject
->MinorSubsystemVersion
= piohOptHeader
->MinorSubsystemVersion
;
499 ImageSectionObject
->MajorSubsystemVersion
= piohOptHeader
->MajorSubsystemVersion
;
503 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, AddressOfEntryPoint
))
505 ImageSectionObject
->EntryPoint
= piohOptHeader
->ImageBase
+
506 piohOptHeader
->AddressOfEntryPoint
;
509 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfCode
))
510 ImageSectionObject
->Executable
= piohOptHeader
->SizeOfCode
!= 0;
512 ImageSectionObject
->Executable
= TRUE
;
514 ImageSectionObject
->ImageCharacteristics
= pinhNtHeader
->FileHeader
.Characteristics
;
515 ImageSectionObject
->Machine
= pinhNtHeader
->FileHeader
.Machine
;
517 /* SECTION HEADERS */
518 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
520 /* see [1], section 3.3 */
521 if(pinhNtHeader
->FileHeader
.NumberOfSections
> 96)
522 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader
->FileHeader
.NumberOfSections
));
525 * the additional segment is for the file's headers. They need to be present for
526 * the benefit of the dynamic loader (to locate exports, defaults for thread
527 * parameters, resources, etc.)
529 ImageSectionObject
->NrSegments
= pinhNtHeader
->FileHeader
.NumberOfSections
+ 1;
531 /* file offset for the section headers */
532 if(!Intsafe_AddULong32(&cbSectionHeadersOffset
, pidhDosHeader
->e_lfanew
, FIELD_OFFSET(IMAGE_NT_HEADERS32
, OptionalHeader
)))
533 DIE(("Offset overflow\n"));
535 if(!Intsafe_AddULong32(&cbSectionHeadersOffset
, cbSectionHeadersOffset
, pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
))
536 DIE(("Offset overflow\n"));
538 /* size of the section headers */
539 ASSERT(Intsafe_CanMulULong32(pinhNtHeader
->FileHeader
.NumberOfSections
, sizeof(IMAGE_SECTION_HEADER
)));
540 cbSectionHeadersSize
= pinhNtHeader
->FileHeader
.NumberOfSections
* sizeof(IMAGE_SECTION_HEADER
);
542 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize
, cbSectionHeadersOffset
, cbSectionHeadersSize
))
543 DIE(("Section headers too large\n"));
545 /* size of the executable's headers */
546 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfHeaders
))
548 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
549 // DIE(("SizeOfHeaders is not aligned\n"));
551 if(cbSectionHeadersSize
> piohOptHeader
->SizeOfHeaders
)
552 DIE(("The section headers overflow SizeOfHeaders\n"));
554 cbHeadersSize
= piohOptHeader
->SizeOfHeaders
;
556 else if(!AlignUp(&cbHeadersSize
, cbSectionHeadersOffsetSize
, nFileAlignment
))
557 DIE(("Overflow aligning the size of headers\n"));
564 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
565 /* WARNING: piohOptHeader IS NO LONGER USABLE */
566 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
568 if(FileHeaderSize
< cbSectionHeadersOffsetSize
)
569 pishSectionHeaders
= NULL
;
573 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
574 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
576 ASSERT(Intsafe_CanOffsetPointer(FileHeader
, cbSectionHeadersOffset
));
577 pishSectionHeaders
= (PVOID
)((UINT_PTR
)FileHeader
+ cbSectionHeadersOffset
);
581 * the buffer doesn't contain the section headers, or the alignment is wrong:
582 * read the headers from the file
584 if(FileHeaderSize
< cbSectionHeadersOffsetSize
||
585 (UINT_PTR
)pishSectionHeaders
% TYPE_ALIGNMENT(IMAGE_SECTION_HEADER
) != 0)
590 lnOffset
.QuadPart
= cbSectionHeadersOffset
;
592 /* read the header from the file */
593 nStatus
= ReadFileCb(File
, &lnOffset
, cbSectionHeadersSize
, &pData
, &pBuffer
, &cbReadSize
);
595 if(!NT_SUCCESS(nStatus
))
596 DIE(("ReadFile failed with status %08X\n", nStatus
));
600 ASSERT(cbReadSize
> 0);
602 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
604 /* the buffer doesn't contain all the section headers */
605 if(cbReadSize
< cbSectionHeadersSize
)
606 DIE(("The file doesn't contain all of the section headers\n"));
608 pishSectionHeaders
= pData
;
610 /* object still not aligned: copy it to the beginning of the buffer */
611 if((UINT_PTR
)pishSectionHeaders
% TYPE_ALIGNMENT(IMAGE_SECTION_HEADER
) != 0)
613 ASSERT((UINT_PTR
)pBuffer
% TYPE_ALIGNMENT(IMAGE_SECTION_HEADER
) == 0);
614 RtlMoveMemory(pBuffer
, pData
, cbReadSize
);
615 pishSectionHeaders
= pBuffer
;
620 /* allocate the segments */
621 nStatus
= STATUS_INSUFFICIENT_RESOURCES
;
622 ImageSectionObject
->Segments
= AllocateSegmentsCb(ImageSectionObject
->NrSegments
);
624 if(ImageSectionObject
->Segments
== NULL
)
625 DIE(("AllocateSegments failed\n"));
627 /* initialize the headers segment */
628 pssSegments
= ImageSectionObject
->Segments
;
630 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
632 if(!AlignUp(&nFileSizeOfHeaders
, cbHeadersSize
, nFileAlignment
))
633 DIE(("Cannot align the size of the section headers\n"));
635 if(!AlignUp(&nPrevVirtualEndOfSegment
, cbHeadersSize
, nSectionAlignment
))
636 DIE(("Cannot align the size of the section headers\n"));
638 pssSegments
[0].FileOffset
= 0;
639 pssSegments
[0].Protection
= PAGE_READONLY
;
640 pssSegments
[0].Length
= nPrevVirtualEndOfSegment
;
641 pssSegments
[0].RawLength
= nFileSizeOfHeaders
;
642 pssSegments
[0].VirtualAddress
= 0;
643 pssSegments
[0].Characteristics
= IMAGE_SCN_CNT_INITIALIZED_DATA
;
644 pssSegments
[0].WriteCopy
= TRUE
;
646 /* skip the headers segment */
649 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
651 /* convert the executable sections into segments. See also [1], section 4 */
652 for(i
= 0; i
< ImageSectionObject
->NrSegments
- 1; ++ i
)
654 ULONG nCharacteristics
;
656 /* validate the alignment */
657 if(!IsAligned(pishSectionHeaders
[i
].VirtualAddress
, nSectionAlignment
))
658 DIE(("VirtualAddress[%u] is not aligned\n", i
));
660 /* sections must be contiguous, ordered by base address and non-overlapping */
661 if(pishSectionHeaders
[i
].VirtualAddress
!= nPrevVirtualEndOfSegment
)
662 DIE(("Memory gap between section %u and the previous\n", i
));
664 /* ignore explicit BSS sections */
665 if(pishSectionHeaders
[i
].SizeOfRawData
!= 0)
667 /* validate the alignment */
669 /* Yes, this should be a multiple of FileAlignment, but there's
670 * stuff out there that isn't. We can cope with that
672 if(!IsAligned(pishSectionHeaders
[i
].SizeOfRawData
, nFileAlignment
))
673 DIE(("SizeOfRawData[%u] is not aligned\n", i
));
676 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
677 // DIE(("PointerToRawData[%u] is not aligned\n", i));
680 pssSegments
[i
].FileOffset
= pishSectionHeaders
[i
].PointerToRawData
;
681 pssSegments
[i
].RawLength
= pishSectionHeaders
[i
].SizeOfRawData
;
685 ASSERT(pssSegments
[i
].FileOffset
== 0);
686 ASSERT(pssSegments
[i
].RawLength
== 0);
689 ASSERT(Intsafe_CanAddLong64(pssSegments
[i
].FileOffset
, pssSegments
[i
].RawLength
));
691 nCharacteristics
= pishSectionHeaders
[i
].Characteristics
;
693 /* no explicit protection */
694 if((nCharacteristics
& (IMAGE_SCN_MEM_EXECUTE
| IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)) == 0)
696 if(nCharacteristics
& IMAGE_SCN_CNT_CODE
)
697 nCharacteristics
|= IMAGE_SCN_MEM_EXECUTE
| IMAGE_SCN_MEM_READ
;
699 if(nCharacteristics
& IMAGE_SCN_CNT_INITIALIZED_DATA
)
700 nCharacteristics
|= IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
;
702 if(nCharacteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
)
703 nCharacteristics
|= IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
;
706 /* see table above */
707 pssSegments
[i
].Protection
= SectionCharacteristicsToProtect
[nCharacteristics
>> 28];
708 pssSegments
[i
].WriteCopy
= !(nCharacteristics
& IMAGE_SCN_MEM_SHARED
);
710 if(pishSectionHeaders
[i
].Misc
.VirtualSize
== 0 || pishSectionHeaders
[i
].Misc
.VirtualSize
< pishSectionHeaders
[i
].SizeOfRawData
)
711 pssSegments
[i
].Length
= pishSectionHeaders
[i
].SizeOfRawData
;
713 pssSegments
[i
].Length
= pishSectionHeaders
[i
].Misc
.VirtualSize
;
715 if(!AlignUp(&pssSegments
[i
].Length
, pssSegments
[i
].Length
, nSectionAlignment
))
716 DIE(("Cannot align the virtual size of section %u\n", i
));
718 ASSERT(IsAligned(pssSegments
[i
].Length
, nSectionAlignment
));
720 if(pssSegments
[i
].Length
== 0)
721 DIE(("Virtual size of section %u is null\n", i
));
723 pssSegments
[i
].VirtualAddress
= pishSectionHeaders
[i
].VirtualAddress
;
724 pssSegments
[i
].Characteristics
= pishSectionHeaders
[i
].Characteristics
;
726 /* ensure the memory image is no larger than 4GB */
727 if(!Intsafe_AddULong32(&nPrevVirtualEndOfSegment
, pssSegments
[i
].VirtualAddress
, pssSegments
[i
].Length
))
728 DIE(("The image is larger than 4GB\n"));
731 /* spare our caller some work in validating the segments */
732 *Flags
= EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED
| EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP
;
734 if(nSectionAlignment
>= PAGE_SIZE
)
735 *Flags
|= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED
;
738 nStatus
= STATUS_ROS_EXEFMT_LOADED_FORMAT
| EXEFMT_LOADED_PE32
;