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>
19 static ULONG SectionCharacteristicsToProtect
[16] =
21 PAGE_NOACCESS
, /* 0 = NONE */
22 PAGE_NOACCESS
, /* 1 = SHARED */
23 PAGE_EXECUTE
, /* 2 = EXECUTABLE */
24 PAGE_EXECUTE
, /* 3 = EXECUTABLE, SHARED */
25 PAGE_READONLY
, /* 4 = READABLE */
26 PAGE_READONLY
, /* 5 = READABLE, SHARED */
27 PAGE_EXECUTE_READ
, /* 6 = READABLE, EXECUTABLE */
28 PAGE_EXECUTE_READ
, /* 7 = READABLE, EXECUTABLE, SHARED */
30 * FIXME? do we really need the WriteCopy field in segments? can't we use
31 * PAGE_WRITECOPY here?
33 PAGE_READWRITE
, /* 8 = WRITABLE */
34 PAGE_READWRITE
, /* 9 = WRITABLE, SHARED */
35 PAGE_EXECUTE_READWRITE
, /* 10 = WRITABLE, EXECUTABLE */
36 PAGE_EXECUTE_READWRITE
, /* 11 = WRITABLE, EXECUTABLE, SHARED */
37 PAGE_READWRITE
, /* 12 = WRITABLE, READABLE */
38 PAGE_READWRITE
, /* 13 = WRITABLE, READABLE, SHARED */
39 PAGE_EXECUTE_READWRITE
, /* 14 = WRITABLE, READABLE, EXECUTABLE */
40 PAGE_EXECUTE_READWRITE
, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
43 /* TODO: Intsafe should be made into a library, as it's generally useful */
44 static __inline BOOLEAN
Intsafe_CanAddULongPtr(IN ULONG_PTR Addend1
, IN ULONG_PTR Addend2
)
46 return Addend1
<= (MAXULONG_PTR
- Addend2
);
49 static __inline BOOLEAN
Intsafe_CanAddLong64(IN LONG64 Addend1
, IN LONG64 Addend2
)
51 return Addend1
<= (MAXLONGLONG
- Addend2
);
54 static __inline BOOLEAN
Intsafe_CanAddULong32(IN ULONG Addend1
, IN ULONG Addend2
)
56 return Addend1
<= (MAXULONG
- Addend2
);
59 static __inline BOOLEAN
Intsafe_AddULong32(OUT PULONG Result
, IN ULONG Addend1
, IN ULONG Addend2
)
61 if(!Intsafe_CanAddULong32(Addend1
, Addend2
))
64 *Result
= Addend1
+ Addend2
;
68 static __inline BOOLEAN
Intsafe_CanMulULong32(IN ULONG Factor1
, IN ULONG Factor2
)
70 return Factor1
<= (MAXULONG
/ Factor2
);
73 static __inline BOOLEAN
Intsafe_CanOffsetPointer(IN CONST VOID
* Pointer
, IN SIZE_T Offset
)
75 /* FIXME: (PVOID)MAXULONG_PTR isn't necessarily a valid address */
76 return Intsafe_CanAddULongPtr((ULONG_PTR
)Pointer
, Offset
);
79 /* TODO: these are standard DDK/PSDK macros */
80 #ifndef RTL_FIELD_SIZE
81 #define RTL_FIELD_SIZE(TYPE_, FIELD_) (sizeof(((TYPE_ *)0)->FIELD_))
84 #ifndef RTL_SIZEOF_THROUGH_FIELD
85 #define RTL_SIZEOF_THROUGH_FIELD(TYPE_, FIELD_) \
86 (FIELD_OFFSET(TYPE_, FIELD_) + RTL_FIELD_SIZE(TYPE_, FIELD_))
89 #ifndef RTL_CONTAINS_FIELD
90 #define RTL_CONTAINS_FIELD(P_, SIZE_, FIELD_) \
91 ((ULONG_PTR)(P_) + (ULONG_PTR)(SIZE_) > (ULONG_PTR)&((P_)->FIELD_) + sizeof((P_)->FIELD_))
94 static __inline BOOLEAN
IsPowerOf2(IN ULONG Number
)
98 return (Number
& (Number
- 1)) == 0;
101 static __inline ULONG
ModPow2(IN ULONG Address
, IN ULONG Alignment
)
103 ASSERT(IsPowerOf2(Alignment
));
104 return Address
& (Alignment
- 1);
107 static __inline BOOLEAN
IsAligned(IN ULONG Address
, IN ULONG Alignment
)
109 return ModPow2(Address
, Alignment
) == 0;
112 static __inline BOOLEAN
AlignUp(OUT PULONG AlignedAddress
, IN ULONG Address
, IN ULONG Alignment
)
114 ULONG nExcess
= ModPow2(Address
, Alignment
);
118 *AlignedAddress
= Address
;
122 return Intsafe_AddULong32(AlignedAddress
, Address
, Alignment
- nExcess
);
125 #define PEFMT_FIELDS_EQUAL(TYPE1_, TYPE2_, FIELD_) \
127 (FIELD_OFFSET(TYPE1_, FIELD_) == FIELD_OFFSET(TYPE2_, FIELD_)) && \
128 (RTL_FIELD_SIZE(TYPE1_, FIELD_) == RTL_FIELD_SIZE(TYPE2_, FIELD_)) \
164 // FIXME: All this whitespace is "padding" so the C_ASSERTs aren't on the same lines as asserts in other headers.
165 // This is necessary because of the way we define C_ASSERT in a gcc compatible way.
166 // This can be removed once we upgrade to gcc 4.3.x or later (which implements __COUNTER__).
196 // PeFmtCreateSection depends on the following:
198 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE
>= sizeof(IMAGE_DOS_HEADER
));
199 C_ASSERT(sizeof(IMAGE_NT_HEADERS32
) <= sizeof(IMAGE_NT_HEADERS64
));
201 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32
) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64
));
202 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32
, FileHeader
) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64
, FileHeader
));
203 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32
, OptionalHeader
) == FIELD_OFFSET(IMAGE_NT_HEADERS64
, OptionalHeader
));
205 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, Magic
));
206 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, SectionAlignment
));
207 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, FileAlignment
));
208 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, Subsystem
));
209 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, MinorSubsystemVersion
));
210 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, MajorSubsystemVersion
));
211 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, AddressOfEntryPoint
));
212 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, SizeOfCode
));
213 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32
, IMAGE_OPTIONAL_HEADER64
, SizeOfHeaders
));
217 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
218 File Format Specification", revision 6.0 (February 1999)
220 NTSTATUS NTAPI
PeFmtCreateSection(IN CONST VOID
* FileHeader
,
221 IN SIZE_T FileHeaderSize
,
223 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject
,
225 IN PEXEFMT_CB_READ_FILE ReadFileCb
,
226 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
)
229 ULONG cbFileHeaderOffsetSize
= 0;
230 ULONG cbSectionHeadersOffset
= 0;
231 ULONG cbSectionHeadersSize
;
232 ULONG cbSectionHeadersOffsetSize
= 0;
233 ULONG cbOptHeaderSize
;
234 ULONG cbHeadersSize
= 0;
235 ULONG nSectionAlignment
;
236 ULONG nFileAlignment
;
237 const IMAGE_DOS_HEADER
* pidhDosHeader
;
238 const IMAGE_NT_HEADERS32
* pinhNtHeader
;
239 const IMAGE_OPTIONAL_HEADER32
* piohOptHeader
;
240 const IMAGE_SECTION_HEADER
* pishSectionHeaders
;
241 PMM_SECTION_SEGMENT pssSegments
;
242 LARGE_INTEGER lnOffset
;
244 ULONG nPrevVirtualEndOfSegment
= 0;
245 ULONG nFileSizeOfHeaders
= 0;
249 ASSERT(FileHeaderSize
> 0);
251 ASSERT(ImageSectionObject
);
253 ASSERT(AllocateSegmentsCb
);
255 ASSERT(Intsafe_CanOffsetPointer(FileHeader
, FileHeaderSize
));
257 ASSERT(((UINT_PTR
)FileHeader
% TYPE_ALIGNMENT(IMAGE_DOS_HEADER
)) == 0);
259 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
262 pidhDosHeader
= FileHeader
;
265 nStatus
= STATUS_ROS_EXEFMT_UNKNOWN_FORMAT
;
267 /* image too small to be an MZ executable */
268 if(FileHeaderSize
< sizeof(IMAGE_DOS_HEADER
))
269 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize
));
271 /* no MZ signature */
272 if(pidhDosHeader
->e_magic
!= IMAGE_DOS_SIGNATURE
)
273 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader
->e_magic
));
275 /* not a Windows executable */
276 if(pidhDosHeader
->e_lfanew
<= 0)
277 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader
->e_lfanew
));
280 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
282 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize
, pidhDosHeader
->e_lfanew
, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32
, FileHeader
)))
283 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader
->e_lfanew
));
285 if(FileHeaderSize
< cbFileHeaderOffsetSize
)
290 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
291 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
293 ASSERT(Intsafe_CanOffsetPointer(FileHeader
, pidhDosHeader
->e_lfanew
));
294 pinhNtHeader
= (PVOID
)((UINT_PTR
)FileHeader
+ pidhDosHeader
->e_lfanew
);
298 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
299 * need to read the header from the file
301 if(FileHeaderSize
< cbFileHeaderOffsetSize
||
302 (UINT_PTR
)pinhNtHeader
% TYPE_ALIGNMENT(IMAGE_NT_HEADERS32
) != 0)
304 ULONG cbNtHeaderSize
;
308 l_ReadHeaderFromFile
:
310 lnOffset
.QuadPart
= pidhDosHeader
->e_lfanew
;
312 /* read the header from the file */
313 nStatus
= ReadFileCb(File
, &lnOffset
, sizeof(IMAGE_NT_HEADERS64
), &pData
, &pBuffer
, &cbReadSize
);
315 if(!NT_SUCCESS(nStatus
))
316 DIE(("ReadFile failed, status %08X\n", nStatus
));
320 ASSERT(cbReadSize
> 0);
322 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
324 /* the buffer doesn't contain the file header */
325 if(cbReadSize
< RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32
, FileHeader
))
326 DIE(("The file doesn't contain the PE file header\n"));
328 pinhNtHeader
= pData
;
330 /* object still not aligned: copy it to the beginning of the buffer */
331 if((UINT_PTR
)pinhNtHeader
% TYPE_ALIGNMENT(IMAGE_NT_HEADERS32
) != 0)
333 ASSERT((UINT_PTR
)pBuffer
% TYPE_ALIGNMENT(IMAGE_NT_HEADERS32
) == 0);
334 RtlMoveMemory(pBuffer
, pData
, cbReadSize
);
335 pinhNtHeader
= pBuffer
;
338 /* invalid NT header */
339 nStatus
= STATUS_INVALID_IMAGE_PROTECT
;
341 if(pinhNtHeader
->Signature
!= IMAGE_NT_SIGNATURE
)
342 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader
->Signature
));
344 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
346 if(!Intsafe_AddULong32(&cbNtHeaderSize
, pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
, FIELD_OFFSET(IMAGE_NT_HEADERS32
, OptionalHeader
)))
347 DIE(("The full NT header is too large\n"));
349 /* the buffer doesn't contain the whole NT header */
350 if(cbReadSize
< cbNtHeaderSize
)
351 DIE(("The file doesn't contain the full NT header\n"));
355 ULONG cbOptHeaderOffsetSize
= 0;
357 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
359 /* don't trust an invalid NT header */
360 if(pinhNtHeader
->Signature
!= IMAGE_NT_SIGNATURE
)
361 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader
->Signature
));
363 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize
, pidhDosHeader
->e_lfanew
, FIELD_OFFSET(IMAGE_NT_HEADERS32
, OptionalHeader
)))
364 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader
->e_lfanew
));
366 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize
, cbOptHeaderOffsetSize
, pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
))
367 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
));
369 /* the buffer doesn't contain the whole NT header: read it from the file */
370 if(cbOptHeaderOffsetSize
> FileHeaderSize
)
371 goto l_ReadHeaderFromFile
;
374 /* read information from the NT header */
375 piohOptHeader
= &pinhNtHeader
->OptionalHeader
;
376 cbOptHeaderSize
= pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
;
378 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
380 if(!RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, Magic
))
381 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize
));
383 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
385 switch(piohOptHeader
->Magic
)
387 case IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
388 case IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
392 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader
->Magic
));
395 if (RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SectionAlignment
) &&
396 RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, FileAlignment
))
398 /* See [1], section 3.4.2 */
399 if(piohOptHeader
->SectionAlignment
< PAGE_SIZE
)
401 if(piohOptHeader
->FileAlignment
!= piohOptHeader
->SectionAlignment
)
402 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
404 else if(piohOptHeader
->SectionAlignment
< piohOptHeader
->FileAlignment
)
405 DIE(("The section alignment is smaller than the file alignment\n"));
407 nSectionAlignment
= piohOptHeader
->SectionAlignment
;
408 nFileAlignment
= piohOptHeader
->FileAlignment
;
410 if(!IsPowerOf2(nSectionAlignment
) || !IsPowerOf2(nFileAlignment
))
411 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment
, nFileAlignment
));
415 nSectionAlignment
= PAGE_SIZE
;
416 nFileAlignment
= PAGE_SIZE
;
419 ASSERT(IsPowerOf2(nSectionAlignment
));
420 ASSERT(IsPowerOf2(nFileAlignment
));
422 switch(piohOptHeader
->Magic
)
425 case IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
427 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, ImageBase
))
428 ImageSectionObject
->ImageBase
= piohOptHeader
->ImageBase
;
430 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfImage
))
431 ImageSectionObject
->ImageSize
= piohOptHeader
->SizeOfImage
;
433 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfStackReserve
))
434 ImageSectionObject
->StackReserve
= piohOptHeader
->SizeOfStackReserve
;
436 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfStackCommit
))
437 ImageSectionObject
->StackCommit
= piohOptHeader
->SizeOfStackCommit
;
443 case IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
445 const IMAGE_OPTIONAL_HEADER64
* pioh64OptHeader
;
447 pioh64OptHeader
= (const IMAGE_OPTIONAL_HEADER64
*)piohOptHeader
;
449 if(RTL_CONTAINS_FIELD(pioh64OptHeader
, cbOptHeaderSize
, ImageBase
))
451 if(pioh64OptHeader
->ImageBase
> MAXULONG_PTR
)
452 DIE(("ImageBase exceeds the address space\n"));
454 ImageSectionObject
->ImageBase
= (ULONG_PTR
)pioh64OptHeader
->ImageBase
;
457 if(RTL_CONTAINS_FIELD(pioh64OptHeader
, cbOptHeaderSize
, SizeOfImage
))
459 if(pioh64OptHeader
->SizeOfImage
> MAXULONG_PTR
)
460 DIE(("SizeOfImage exceeds the address space\n"));
462 ImageSectionObject
->ImageSize
= pioh64OptHeader
->SizeOfImage
;
465 if(RTL_CONTAINS_FIELD(pioh64OptHeader
, cbOptHeaderSize
, SizeOfStackReserve
))
467 if(pioh64OptHeader
->SizeOfStackReserve
> MAXULONG_PTR
)
468 DIE(("SizeOfStackReserve exceeds the address space\n"));
470 ImageSectionObject
->StackReserve
= pioh64OptHeader
->SizeOfStackReserve
;
473 if(RTL_CONTAINS_FIELD(pioh64OptHeader
, cbOptHeaderSize
, SizeOfStackCommit
))
475 if(pioh64OptHeader
->SizeOfStackCommit
> MAXULONG_PTR
)
476 DIE(("SizeOfStackCommit exceeds the address space\n"));
478 ImageSectionObject
->StackCommit
= pioh64OptHeader
->SizeOfStackCommit
;
485 /* [1], section 3.4.2 */
486 if((ULONG_PTR
)ImageSectionObject
->ImageBase
% 0x10000)
487 DIE(("ImageBase is not aligned on a 64KB boundary"));
489 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, Subsystem
))
491 ImageSectionObject
->Subsystem
= piohOptHeader
->Subsystem
;
493 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, MinorSubsystemVersion
) &&
494 RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, MajorSubsystemVersion
))
496 ImageSectionObject
->MinorSubsystemVersion
= piohOptHeader
->MinorSubsystemVersion
;
497 ImageSectionObject
->MajorSubsystemVersion
= piohOptHeader
->MajorSubsystemVersion
;
501 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, AddressOfEntryPoint
))
503 ImageSectionObject
->EntryPoint
= piohOptHeader
->ImageBase
+
504 piohOptHeader
->AddressOfEntryPoint
;
507 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfCode
))
508 ImageSectionObject
->Executable
= piohOptHeader
->SizeOfCode
!= 0;
510 ImageSectionObject
->Executable
= TRUE
;
512 ImageSectionObject
->ImageCharacteristics
= pinhNtHeader
->FileHeader
.Characteristics
;
513 ImageSectionObject
->Machine
= pinhNtHeader
->FileHeader
.Machine
;
515 /* SECTION HEADERS */
516 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
518 /* see [1], section 3.3 */
519 if(pinhNtHeader
->FileHeader
.NumberOfSections
> 96)
520 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader
->FileHeader
.NumberOfSections
));
523 * the additional segment is for the file's headers. They need to be present for
524 * the benefit of the dynamic loader (to locate exports, defaults for thread
525 * parameters, resources, etc.)
527 ImageSectionObject
->NrSegments
= pinhNtHeader
->FileHeader
.NumberOfSections
+ 1;
529 /* file offset for the section headers */
530 if(!Intsafe_AddULong32(&cbSectionHeadersOffset
, pidhDosHeader
->e_lfanew
, FIELD_OFFSET(IMAGE_NT_HEADERS32
, OptionalHeader
)))
531 DIE(("Offset overflow\n"));
533 if(!Intsafe_AddULong32(&cbSectionHeadersOffset
, cbSectionHeadersOffset
, pinhNtHeader
->FileHeader
.SizeOfOptionalHeader
))
534 DIE(("Offset overflow\n"));
536 /* size of the section headers */
537 ASSERT(Intsafe_CanMulULong32(pinhNtHeader
->FileHeader
.NumberOfSections
, sizeof(IMAGE_SECTION_HEADER
)));
538 cbSectionHeadersSize
= pinhNtHeader
->FileHeader
.NumberOfSections
* sizeof(IMAGE_SECTION_HEADER
);
540 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize
, cbSectionHeadersOffset
, cbSectionHeadersSize
))
541 DIE(("Section headers too large\n"));
543 /* size of the executable's headers */
544 if(RTL_CONTAINS_FIELD(piohOptHeader
, cbOptHeaderSize
, SizeOfHeaders
))
546 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
547 // DIE(("SizeOfHeaders is not aligned\n"));
549 if(cbSectionHeadersSize
> piohOptHeader
->SizeOfHeaders
)
550 DIE(("The section headers overflow SizeOfHeaders\n"));
552 cbHeadersSize
= piohOptHeader
->SizeOfHeaders
;
554 else if(!AlignUp(&cbHeadersSize
, cbSectionHeadersOffsetSize
, nFileAlignment
))
555 DIE(("Overflow aligning the size of headers\n"));
562 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
563 /* WARNING: piohOptHeader IS NO LONGER USABLE */
564 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
566 if(FileHeaderSize
< cbSectionHeadersOffsetSize
)
567 pishSectionHeaders
= NULL
;
571 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
572 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
574 ASSERT(Intsafe_CanOffsetPointer(FileHeader
, cbSectionHeadersOffset
));
575 pishSectionHeaders
= (PVOID
)((UINT_PTR
)FileHeader
+ cbSectionHeadersOffset
);
579 * the buffer doesn't contain the section headers, or the alignment is wrong:
580 * read the headers from the file
582 if(FileHeaderSize
< cbSectionHeadersOffsetSize
||
583 (UINT_PTR
)pishSectionHeaders
% TYPE_ALIGNMENT(IMAGE_SECTION_HEADER
) != 0)
588 lnOffset
.QuadPart
= cbSectionHeadersOffset
;
590 /* read the header from the file */
591 nStatus
= ReadFileCb(File
, &lnOffset
, cbSectionHeadersSize
, &pData
, &pBuffer
, &cbReadSize
);
593 if(!NT_SUCCESS(nStatus
))
594 DIE(("ReadFile failed with status %08X\n", nStatus
));
598 ASSERT(cbReadSize
> 0);
600 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
602 /* the buffer doesn't contain all the section headers */
603 if(cbReadSize
< cbSectionHeadersSize
)
604 DIE(("The file doesn't contain all of the section headers\n"));
606 pishSectionHeaders
= pData
;
608 /* object still not aligned: copy it to the beginning of the buffer */
609 if((UINT_PTR
)pishSectionHeaders
% TYPE_ALIGNMENT(IMAGE_SECTION_HEADER
) != 0)
611 ASSERT((UINT_PTR
)pBuffer
% TYPE_ALIGNMENT(IMAGE_SECTION_HEADER
) == 0);
612 RtlMoveMemory(pBuffer
, pData
, cbReadSize
);
613 pishSectionHeaders
= pBuffer
;
618 /* allocate the segments */
619 nStatus
= STATUS_INSUFFICIENT_RESOURCES
;
620 ImageSectionObject
->Segments
= AllocateSegmentsCb(ImageSectionObject
->NrSegments
);
622 if(ImageSectionObject
->Segments
== NULL
)
623 DIE(("AllocateSegments failed\n"));
625 /* initialize the headers segment */
626 pssSegments
= ImageSectionObject
->Segments
;
628 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
630 if(!AlignUp(&nFileSizeOfHeaders
, cbHeadersSize
, nFileAlignment
))
631 DIE(("Cannot align the size of the section headers\n"));
633 if(!AlignUp(&nPrevVirtualEndOfSegment
, cbHeadersSize
, nSectionAlignment
))
634 DIE(("Cannot align the size of the section headers\n"));
636 pssSegments
[0].FileOffset
= 0;
637 pssSegments
[0].Protection
= PAGE_READONLY
;
638 pssSegments
[0].Length
= nPrevVirtualEndOfSegment
;
639 pssSegments
[0].RawLength
= nFileSizeOfHeaders
;
640 pssSegments
[0].VirtualAddress
= 0;
641 pssSegments
[0].Characteristics
= IMAGE_SCN_CNT_INITIALIZED_DATA
;
642 pssSegments
[0].WriteCopy
= TRUE
;
644 /* skip the headers segment */
647 nStatus
= STATUS_INVALID_IMAGE_FORMAT
;
649 /* convert the executable sections into segments. See also [1], section 4 */
650 for(i
= 0; i
< ImageSectionObject
->NrSegments
- 1; ++ i
)
652 ULONG nCharacteristics
;
654 /* validate the alignment */
655 if(!IsAligned(pishSectionHeaders
[i
].VirtualAddress
, nSectionAlignment
))
656 DIE(("VirtualAddress[%u] is not aligned\n", i
));
658 /* sections must be contiguous, ordered by base address and non-overlapping */
659 if(pishSectionHeaders
[i
].VirtualAddress
!= nPrevVirtualEndOfSegment
)
660 DIE(("Memory gap between section %u and the previous\n", i
));
662 /* ignore explicit BSS sections */
663 if(pishSectionHeaders
[i
].SizeOfRawData
!= 0)
665 /* validate the alignment */
667 /* Yes, this should be a multiple of FileAlignment, but there's
668 * stuff out there that isn't. We can cope with that
670 if(!IsAligned(pishSectionHeaders
[i
].SizeOfRawData
, nFileAlignment
))
671 DIE(("SizeOfRawData[%u] is not aligned\n", i
));
674 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
675 // DIE(("PointerToRawData[%u] is not aligned\n", i));
678 pssSegments
[i
].FileOffset
= pishSectionHeaders
[i
].PointerToRawData
;
679 pssSegments
[i
].RawLength
= pishSectionHeaders
[i
].SizeOfRawData
;
683 ASSERT(pssSegments
[i
].FileOffset
== 0);
684 ASSERT(pssSegments
[i
].RawLength
== 0);
687 ASSERT(Intsafe_CanAddLong64(pssSegments
[i
].FileOffset
, pssSegments
[i
].RawLength
));
689 nCharacteristics
= pishSectionHeaders
[i
].Characteristics
;
691 /* no explicit protection */
692 if((nCharacteristics
& (IMAGE_SCN_MEM_EXECUTE
| IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)) == 0)
694 if(nCharacteristics
& IMAGE_SCN_CNT_CODE
)
695 nCharacteristics
|= IMAGE_SCN_MEM_EXECUTE
| IMAGE_SCN_MEM_READ
;
697 if(nCharacteristics
& IMAGE_SCN_CNT_INITIALIZED_DATA
)
698 nCharacteristics
|= IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
;
700 if(nCharacteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
)
701 nCharacteristics
|= IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
;
704 /* see table above */
705 pssSegments
[i
].Protection
= SectionCharacteristicsToProtect
[nCharacteristics
>> 28];
706 pssSegments
[i
].WriteCopy
= !(nCharacteristics
& IMAGE_SCN_MEM_SHARED
);
708 if(pishSectionHeaders
[i
].Misc
.VirtualSize
== 0 || pishSectionHeaders
[i
].Misc
.VirtualSize
< pishSectionHeaders
[i
].SizeOfRawData
)
709 pssSegments
[i
].Length
= pishSectionHeaders
[i
].SizeOfRawData
;
711 pssSegments
[i
].Length
= pishSectionHeaders
[i
].Misc
.VirtualSize
;
713 if(!AlignUp(&pssSegments
[i
].Length
, pssSegments
[i
].Length
, nSectionAlignment
))
714 DIE(("Cannot align the virtual size of section %u\n", i
));
716 ASSERT(IsAligned(pssSegments
[i
].Length
, nSectionAlignment
));
718 if(pssSegments
[i
].Length
== 0)
719 DIE(("Virtual size of section %u is null\n", i
));
721 pssSegments
[i
].VirtualAddress
= pishSectionHeaders
[i
].VirtualAddress
;
722 pssSegments
[i
].Characteristics
= pishSectionHeaders
[i
].Characteristics
;
724 /* ensure the memory image is no larger than 4GB */
725 if(!Intsafe_AddULong32(&nPrevVirtualEndOfSegment
, pssSegments
[i
].VirtualAddress
, pssSegments
[i
].Length
))
726 DIE(("The image is larger than 4GB\n"));
729 /* spare our caller some work in validating the segments */
730 *Flags
= EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED
| EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP
;
732 if(nSectionAlignment
>= PAGE_SIZE
)
733 *Flags
|= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED
;
736 nStatus
= STATUS_ROS_EXEFMT_LOADED_FORMAT
| EXEFMT_LOADED_PE32
;