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