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