strip whitespace from end of lines
[reactos.git] / reactos / ntoskrnl / mm / pe.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/mm/pe.c
6 * PURPOSE: Loader for PE executables
7 *
8 * PROGRAMMERS: KJK::Hyperion <hackbunny@reactos.com>
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14
15 //#define NDEBUG
16 #include <internal/debug.h>
17
18 #include <reactos/exeformat.h>
19
20 static ULONG SectionCharacteristicsToProtect[16] =
21 {
22 PAGE_NOACCESS, /* 0 = NONE */
23 PAGE_NOACCESS, /* 1 = SHARED */
24 PAGE_EXECUTE, /* 2 = EXECUTABLE */
25 PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
26 PAGE_READONLY, /* 4 = READABLE */
27 PAGE_READONLY, /* 5 = READABLE, SHARED */
28 PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
29 PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
30 /*
31 * FIXME? do we really need the WriteCopy field in segments? can't we use
32 * PAGE_WRITECOPY here?
33 */
34 PAGE_READWRITE, /* 8 = WRITABLE */
35 PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
36 PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
37 PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
38 PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
39 PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
40 PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
41 PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
42 };
43
44 /* TODO: Intsafe should be made into a library, as it's generally useful */
45 static __inline BOOLEAN Intsafe_CanAddULongPtr
46 (
47 IN ULONG_PTR Addend1,
48 IN ULONG_PTR Addend2
49 )
50 {
51 return Addend1 <= (MAXULONG_PTR - Addend2);
52 }
53
54 #ifndef MAXLONGLONG
55 #define MAXLONGLONG ((LONGLONG)((~((ULONGLONG)0)) >> 1))
56 #endif
57
58 static __inline BOOLEAN Intsafe_CanAddLong64
59 (
60 IN LONG64 Addend1,
61 IN LONG64 Addend2
62 )
63 {
64 return Addend1 <= (MAXLONGLONG - Addend2);
65 }
66
67 static __inline BOOLEAN Intsafe_CanAddULong32
68 (
69 IN ULONG Addend1,
70 IN ULONG Addend2
71 )
72 {
73 return Addend1 <= (MAXULONG - Addend2);
74 }
75
76 static __inline BOOLEAN Intsafe_AddULong32
77 (
78 OUT PULONG Result,
79 IN ULONG Addend1,
80 IN ULONG Addend2
81 )
82 {
83 if(!Intsafe_CanAddULong32(Addend1, Addend2))
84 return FALSE;
85
86 *Result = Addend1 + Addend2;
87 return TRUE;
88 }
89
90 static __inline BOOLEAN Intsafe_CanMulULong32
91 (
92 IN ULONG Factor1,
93 IN ULONG Factor2
94 )
95 {
96 return Factor1 <= (MAXULONG / Factor2);
97 }
98
99 static __inline BOOLEAN Intsafe_CanOffsetPointer
100 (
101 IN CONST VOID * Pointer,
102 IN SIZE_T Offset
103 )
104 {
105 /* FIXME: (PVOID)MAXULONG_PTR isn't necessarily a valid address */
106 return Intsafe_CanAddULongPtr((ULONG_PTR)Pointer, Offset);
107 }
108
109 /* TODO: these are standard DDK/PSDK macros */
110 #ifndef RTL_FIELD_SIZE
111 #define RTL_FIELD_SIZE(TYPE_, FIELD_) (sizeof(((TYPE_ *)0)->FIELD_))
112 #endif
113
114 #ifndef RTL_SIZEOF_THROUGH_FIELD
115 #define RTL_SIZEOF_THROUGH_FIELD(TYPE_, FIELD_) \
116 (FIELD_OFFSET(TYPE_, FIELD_) + RTL_FIELD_SIZE(TYPE_, FIELD_))
117 #endif
118
119 #ifndef RTL_CONTAINS_FIELD
120 #define RTL_CONTAINS_FIELD(P_, SIZE_, FIELD_) \
121 ((((char *)(P_)) + (SIZE_)) > (((char *)(&((P_)->FIELD_))) + sizeof((P_)->FIELD_)))
122 #endif
123
124 static __inline BOOLEAN IsPowerOf2(IN ULONG Number)
125 {
126 if(Number == 0)
127 return FALSE;
128
129 while((Number % 2) == 0)
130 Number /= 2;
131
132 return Number == 1;
133 }
134
135 static __inline ULONG ModPow2(IN ULONG Address, IN ULONG Alignment)
136 {
137 ASSERT(IsPowerOf2(Alignment));
138 return Address & (Alignment - 1);
139 }
140
141 static __inline BOOLEAN IsAligned(IN ULONG Address, IN ULONG Alignment)
142 {
143 return ModPow2(Address, Alignment) == 0;
144 }
145
146 static __inline BOOLEAN AlignUp
147 (
148 OUT PULONG AlignedAddress,
149 IN ULONG Address,
150 IN ULONG Alignment
151 )
152 {
153 ULONG nExcess = ModPow2(Address, Alignment);
154
155 if(nExcess == 0)
156 {
157 *AlignedAddress = Address;
158 return nExcess == 0;
159 }
160 else
161 return Intsafe_AddULong32(AlignedAddress, Address, Alignment - nExcess);
162 }
163
164 #define PEFMT_FIELDS_EQUAL(TYPE1_, TYPE2_, FIELD_) \
165 ( \
166 (FIELD_OFFSET(TYPE1_, FIELD_) == FIELD_OFFSET(TYPE2_, FIELD_)) && \
167 (RTL_FIELD_SIZE(TYPE1_, FIELD_) == RTL_FIELD_SIZE(TYPE2_, FIELD_)) \
168 )
169
170 /*
171 References:
172 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
173 File Format Specification", revision 6.0 (February 1999)
174 */
175 NTSTATUS NTAPI PeFmtCreateSection
176 (
177 IN CONST VOID * FileHeader,
178 IN SIZE_T FileHeaderSize,
179 IN PVOID File,
180 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
181 OUT PULONG Flags,
182 IN PEXEFMT_CB_READ_FILE ReadFileCb,
183 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
184 )
185 {
186 NTSTATUS nStatus;
187 ULONG cbFileHeaderOffsetSize;
188 ULONG cbSectionHeadersOffset;
189 ULONG cbSectionHeadersSize;
190 ULONG cbSectionHeadersOffsetSize;
191 ULONG cbOptHeaderSize;
192 ULONG cbHeadersSize;
193 ULONG nSectionAlignment;
194 ULONG nFileAlignment;
195 const IMAGE_DOS_HEADER * pidhDosHeader;
196 const IMAGE_NT_HEADERS32 * pinhNtHeader;
197 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
198 const IMAGE_SECTION_HEADER * pishSectionHeaders;
199 PMM_SECTION_SEGMENT pssSegments;
200 LARGE_INTEGER lnOffset;
201 PVOID pBuffer;
202 ULONG nPrevVirtualEndOfSegment;
203 ULONG nFileSizeOfHeaders;
204 ULONG i;
205
206 ASSERT(FileHeader);
207 ASSERT(FileHeaderSize > 0);
208 ASSERT(File);
209 ASSERT(ImageSectionObject);
210 ASSERT(ReadFileCb);
211 ASSERT(AllocateSegmentsCb);
212
213 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
214
215 ASSERT(FileHeaderSize >= sizeof(IMAGE_DOS_HEADER));
216 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
217
218 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
219
220 pBuffer = NULL;
221 pidhDosHeader = FileHeader;
222
223 /* DOS HEADER */
224 nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
225
226 /* no MZ signature */
227 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
228 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
229
230 /* not a Windows executable */
231 if(pidhDosHeader->e_lfanew <= 0)
232 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
233
234 /* NT HEADER */
235 nStatus = STATUS_INVALID_IMAGE_FORMAT;
236
237 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
238 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
239
240 if(FileHeaderSize < cbFileHeaderOffsetSize)
241 pinhNtHeader = NULL;
242 else
243 {
244 /*
245 we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
246 and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
247 */
248 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
249 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
250 }
251
252 ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
253 ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
254 ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
255 ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
256
257 /*
258 the buffer doesn't contain the NT file header, or the alignment is wrong: we
259 need to read the header from the file
260 */
261 if
262 (
263 FileHeaderSize < cbFileHeaderOffsetSize ||
264 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0
265 )
266 {
267 ULONG cbNtHeaderSize;
268 ULONG cbReadSize;
269 PVOID pData;
270
271 l_ReadHeaderFromFile:
272 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
273
274 /* read the header from the file */
275 nStatus = ReadFileCb
276 (
277 File,
278 &lnOffset,
279 sizeof(IMAGE_NT_HEADERS64),
280 &pData,
281 &pBuffer,
282 &cbReadSize
283 );
284
285 if(!NT_SUCCESS(nStatus))
286 DIE(("ReadFile failed, status %08X\n", nStatus));
287
288 ASSERT(pData);
289 ASSERT(pBuffer);
290 ASSERT(cbReadSize > 0);
291
292 nStatus = STATUS_INVALID_IMAGE_FORMAT;
293
294 /* the buffer doesn't contain the file header */
295 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
296 DIE(("The file doesn't contain the PE file header\n"));
297
298 pinhNtHeader = pData;
299
300 /* object still not aligned: copy it to the beginning of the buffer */
301 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
302 {
303 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
304 RtlMoveMemory(pBuffer, pData, cbReadSize);
305 pinhNtHeader = pBuffer;
306 }
307
308 /* invalid NT header */
309 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
310 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
311
312 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
313 DIE(("The full NT header is too large\n"));
314
315 nStatus = STATUS_UNSUCCESSFUL;
316
317 /* the buffer doesn't contain the whole NT header */
318 if(cbReadSize < cbNtHeaderSize)
319 DIE(("The file doesn't contain the full NT header\n"));
320 }
321 else
322 {
323 ULONG cbOptHeaderOffsetSize;
324
325 nStatus = STATUS_INVALID_IMAGE_FORMAT;
326
327 /* don't trust an invalid NT header */
328 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
329 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
330
331 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
332 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
333
334 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
335 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
336
337 /* the buffer doesn't contain the whole NT header: read it from the file */
338 if(cbOptHeaderOffsetSize > FileHeaderSize)
339 goto l_ReadHeaderFromFile;
340 }
341
342 /* read information from the NT header */
343 piohOptHeader = &pinhNtHeader->OptionalHeader;
344 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
345
346 nStatus = STATUS_INVALID_IMAGE_FORMAT;
347
348 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
349
350 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
351 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
352
353 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
354
355 switch(piohOptHeader->Magic)
356 {
357 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
358 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
359 break;
360
361 default:
362 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
363 }
364
365 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
366 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
367
368 if
369 (
370 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
371 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment)
372 )
373 {
374 /* See [1], section 3.4.2 */
375 if(piohOptHeader->SectionAlignment < PAGE_SIZE)
376 {
377 if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
378 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
379 }
380 else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
381 DIE(("The section alignment is smaller than the file alignment\n"));
382
383 nSectionAlignment = piohOptHeader->SectionAlignment;
384 nFileAlignment = piohOptHeader->FileAlignment;
385
386 if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
387 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
388 }
389 else
390 {
391 nSectionAlignment = PAGE_SIZE;
392 nFileAlignment = PAGE_SIZE;
393 }
394
395 ASSERT(IsPowerOf2(nSectionAlignment));
396 ASSERT(IsPowerOf2(nFileAlignment));
397
398 switch(piohOptHeader->Magic)
399 {
400 /* PE32 */
401 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
402 {
403 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
404 ImageSectionObject->ImageBase = piohOptHeader->ImageBase;
405
406 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
407 ImageSectionObject->StackReserve = piohOptHeader->SizeOfStackReserve;
408
409 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
410 ImageSectionObject->StackCommit = piohOptHeader->SizeOfStackCommit;
411
412 break;
413 }
414
415 /* PE32+ */
416 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
417 {
418 const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
419
420 pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
421
422 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
423 {
424 if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
425 DIE(("ImageBase exceeds the address space\n"));
426
427 ImageSectionObject->ImageBase = pioh64OptHeader->ImageBase;
428 }
429
430 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
431 {
432 if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
433 DIE(("SizeOfStackReserve exceeds the address space\n"));
434
435 ImageSectionObject->StackReserve = pioh64OptHeader->SizeOfStackReserve;
436 }
437
438 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
439 {
440 if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
441 DIE(("SizeOfStackCommit exceeds the address space\n"));
442
443 ImageSectionObject->StackCommit = pioh64OptHeader->SizeOfStackCommit;
444 }
445
446 break;
447 }
448 }
449
450 /* [1], section 3.4.2 */
451 if((ULONG_PTR)ImageSectionObject->ImageBase % 0x10000)
452 DIE(("ImageBase is not aligned on a 64KB boundary"));
453
454 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
455 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
456 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
457
458 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
459 {
460 ImageSectionObject->Subsystem = piohOptHeader->Subsystem;
461
462 if
463 (
464 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
465 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion)
466 )
467 {
468 ImageSectionObject->MinorSubsystemVersion = piohOptHeader->MinorSubsystemVersion;
469 ImageSectionObject->MajorSubsystemVersion = piohOptHeader->MajorSubsystemVersion;
470 }
471 }
472
473 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
474
475 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
476 ImageSectionObject->EntryPoint = piohOptHeader->AddressOfEntryPoint;
477
478 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
479
480 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
481 ImageSectionObject->Executable = piohOptHeader->SizeOfCode != 0;
482 else
483 ImageSectionObject->Executable = TRUE;
484
485 ImageSectionObject->ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
486 ImageSectionObject->Machine = pinhNtHeader->FileHeader.Machine;
487
488 /* SECTION HEADERS */
489 nStatus = STATUS_INVALID_IMAGE_FORMAT;
490
491 /* see [1], section 3.3 */
492 if(pinhNtHeader->FileHeader.NumberOfSections > 96)
493 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
494
495 /*
496 the additional segment is for the file's headers. They need to be present for
497 the benefit of the dynamic loader (to locate exports, defaults for thread
498 parameters, resources, etc.)
499 */
500 ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
501
502 /* file offset for the section headers */
503 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
504 DIE(("Offset overflow\n"));
505
506 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
507 DIE(("Offset overflow\n"));
508
509 /* size of the section headers */
510 ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
511 cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
512
513 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
514 DIE(("Section headers too large\n"));
515
516 ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
517
518 /* size of the executable's headers */
519 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
520 {
521 if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
522 DIE(("SizeOfHeaders is not aligned\n"));
523
524 if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
525 DIE(("The section headers overflow SizeOfHeaders\n"));
526
527 cbHeadersSize = piohOptHeader->SizeOfHeaders;
528 }
529 else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
530 DIE(("Overflow aligning the size of headers\n"));
531
532 if(pBuffer)
533 {
534 ExFreePool(pBuffer);
535 pBuffer = NULL;
536 }
537 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
538 /* WARNING: piohOptHeader IS NO LONGER USABLE */
539 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
540
541 if(FileHeaderSize < cbSectionHeadersOffsetSize)
542 pishSectionHeaders = NULL;
543 else
544 {
545 /*
546 we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
547 and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
548 */
549 ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
550 pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
551 }
552
553 /*
554 the buffer doesn't contain the section headers, or the alignment is wrong:
555 read the headers from the file
556 */
557 if
558 (
559 FileHeaderSize < cbSectionHeadersOffsetSize ||
560 (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0
561 )
562 {
563 PVOID pData;
564 ULONG cbReadSize;
565
566 lnOffset.QuadPart = cbSectionHeadersOffset;
567
568 /* read the header from the file */
569 nStatus = ReadFileCb
570 (
571 File,
572 &lnOffset,
573 cbSectionHeadersSize,
574 &pData,
575 &pBuffer,
576 &cbReadSize
577 );
578
579 if(!NT_SUCCESS(nStatus))
580 DIE(("ReadFile failed with status %08X\n", nStatus));
581
582 ASSERT(pData);
583 ASSERT(pBuffer);
584 ASSERT(cbReadSize > 0);
585
586 nStatus = STATUS_INVALID_IMAGE_FORMAT;
587
588 /* the buffer doesn't contain all the section headers */
589 if(cbReadSize < cbSectionHeadersSize)
590 DIE(("The file doesn't contain all of the section headers\n"));
591
592 pishSectionHeaders = pData;
593
594 /* object still not aligned: copy it to the beginning of the buffer */
595 if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
596 {
597 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
598 RtlMoveMemory(pBuffer, pData, cbReadSize);
599 pishSectionHeaders = pBuffer;
600 }
601 }
602
603 /* SEGMENTS */
604 /* allocate the segments */
605 nStatus = STATUS_INSUFFICIENT_RESOURCES;
606 ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
607
608 if(ImageSectionObject->Segments == NULL)
609 DIE(("AllocateSegments failed\n"));
610
611 /* initialize the headers segment */
612 pssSegments = ImageSectionObject->Segments;
613
614 ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
615
616 if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
617 DIE(("Cannot align the size of the section headers\n"));
618
619 if(!AlignUp(&nPrevVirtualEndOfSegment, cbHeadersSize, nSectionAlignment))
620 DIE(("Cannot align the size of the section headers\n"));
621
622 pssSegments[0].FileOffset = 0;
623 pssSegments[0].Protection = PAGE_READONLY;
624 pssSegments[0].Length = nPrevVirtualEndOfSegment;
625 pssSegments[0].RawLength = nFileSizeOfHeaders;
626 pssSegments[0].VirtualAddress = 0;
627 pssSegments[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA;
628 pssSegments[0].WriteCopy = TRUE;
629
630 /* skip the headers segment */
631 ++ pssSegments;
632
633 nStatus = STATUS_INVALID_IMAGE_FORMAT;
634
635 /* convert the executable sections into segments. See also [1], section 4 */
636 for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
637 {
638 ULONG nCharacteristics;
639
640 /* validate the alignment */
641 if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
642 DIE(("VirtualAddress[%u] is not aligned\n", i));
643
644 /* sections must be contiguous, ordered by base address and non-overlapping */
645 if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
646 DIE(("Memory gap between section %u and the previous\n", i));
647
648 /* ignore explicit BSS sections */
649 if(pishSectionHeaders[i].SizeOfRawData != 0)
650 {
651 /* validate the alignment */
652 #if 0 /* Yes, this should be a multiple of FileAlignment, but there's
653 stuff out there that isn't. We can cope with that */
654 if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
655 DIE(("SizeOfRawData[%u] is not aligned\n", i));
656 #endif
657
658 if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
659 DIE(("PointerToRawData[%u] is not aligned\n", i));
660
661 /* conversion */
662 pssSegments[i].FileOffset = pishSectionHeaders[i].PointerToRawData;
663 pssSegments[i].RawLength = pishSectionHeaders[i].SizeOfRawData;
664 }
665 else
666 {
667 ASSERT(pssSegments[i].FileOffset == 0);
668 ASSERT(pssSegments[i].RawLength == 0);
669 }
670
671 ASSERT(Intsafe_CanAddLong64(pssSegments[i].FileOffset, pssSegments[i].RawLength));
672
673 nCharacteristics = pishSectionHeaders[i].Characteristics;
674
675 /* no explicit protection */
676 if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
677 {
678 if(nCharacteristics & IMAGE_SCN_CNT_CODE)
679 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
680
681 if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
682 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
683
684 if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
685 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
686 }
687
688 /* see table above */
689 pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
690 pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
691
692 if(pishSectionHeaders[i].Misc.VirtualSize == 0 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData)
693 pssSegments[i].Length = pishSectionHeaders[i].SizeOfRawData;
694 else
695 pssSegments[i].Length = pishSectionHeaders[i].Misc.VirtualSize;
696
697 if(!AlignUp(&pssSegments[i].Length, pssSegments[i].Length, nSectionAlignment))
698 DIE(("Cannot align the virtual size of section %u\n", i));
699
700 ASSERT(IsAligned(pssSegments[i].Length, nSectionAlignment));
701
702 if(pssSegments[i].Length == 0)
703 DIE(("Virtual size of section %u is null\n", i));
704
705 pssSegments[i].VirtualAddress = pishSectionHeaders[i].VirtualAddress;
706 pssSegments[i].Characteristics = pishSectionHeaders[i].Characteristics;
707
708 /* ensure the memory image is no larger than 4GB */
709 if(!Intsafe_AddULong32(&nPrevVirtualEndOfSegment, pssSegments[i].VirtualAddress, pssSegments[i].Length))
710 DIE(("The image is larger than 4GB\n"));
711 }
712
713 /* spare our caller some work in validating the segments */
714 *Flags = EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED | EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP;
715
716 if(nSectionAlignment >= PAGE_SIZE)
717 *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
718
719 /* Success */
720 nStatus = STATUS_ROS_EXEFMT_LOADED_FORMAT & EXEFMT_LOADED_PE32;
721
722 l_Return:
723 if(pBuffer)
724 ExFreePool(pBuffer);
725
726 return nStatus;
727 }
728
729 /* EOF */