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