sync to trunk revision 36500
[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 References:
141 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
142 File Format Specification", revision 6.0 (February 1999)
143 */
144 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
145 IN SIZE_T FileHeaderSize,
146 IN PVOID File,
147 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
148 OUT PULONG Flags,
149 IN PEXEFMT_CB_READ_FILE ReadFileCb,
150 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
151 {
152 NTSTATUS nStatus;
153 ULONG cbFileHeaderOffsetSize = 0;
154 ULONG cbSectionHeadersOffset = 0;
155 ULONG cbSectionHeadersSize;
156 ULONG cbSectionHeadersOffsetSize = 0;
157 ULONG cbOptHeaderSize;
158 ULONG cbHeadersSize = 0;
159 ULONG nSectionAlignment;
160 ULONG nFileAlignment;
161 const IMAGE_DOS_HEADER * pidhDosHeader;
162 const IMAGE_NT_HEADERS32 * pinhNtHeader;
163 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
164 const IMAGE_SECTION_HEADER * pishSectionHeaders;
165 PMM_SECTION_SEGMENT pssSegments;
166 LARGE_INTEGER lnOffset;
167 PVOID pBuffer;
168 ULONG nPrevVirtualEndOfSegment = 0;
169 ULONG nFileSizeOfHeaders = 0;
170 ULONG i;
171
172 ASSERT(FileHeader);
173 ASSERT(FileHeaderSize > 0);
174 ASSERT(File);
175 ASSERT(ImageSectionObject);
176 ASSERT(ReadFileCb);
177 ASSERT(AllocateSegmentsCb);
178
179 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
180
181 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
182 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
183
184 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
185
186 pBuffer = NULL;
187 pidhDosHeader = FileHeader;
188
189 /* DOS HEADER */
190 nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
191
192 /* image too small to be an MZ executable */
193 if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
194 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
195
196 /* no MZ signature */
197 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
198 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
199
200 /* not a Windows executable */
201 if(pidhDosHeader->e_lfanew <= 0)
202 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
203
204 /* NT HEADER */
205 nStatus = STATUS_INVALID_IMAGE_FORMAT;
206
207 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
208 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
209
210 if(FileHeaderSize < cbFileHeaderOffsetSize)
211 pinhNtHeader = NULL;
212 else
213 {
214 /*
215 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
216 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
217 */
218 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
219 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
220 }
221
222 C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
223 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
224 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
225 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
226
227 /*
228 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
229 * need to read the header from the file
230 */
231 if(FileHeaderSize < cbFileHeaderOffsetSize ||
232 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
233 {
234 ULONG cbNtHeaderSize;
235 ULONG cbReadSize;
236 PVOID pData;
237
238 l_ReadHeaderFromFile:
239 cbNtHeaderSize = 0;
240 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
241
242 /* read the header from the file */
243 nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
244
245 if(!NT_SUCCESS(nStatus))
246 DIE(("ReadFile failed, status %08X\n", nStatus));
247
248 ASSERT(pData);
249 ASSERT(pBuffer);
250 ASSERT(cbReadSize > 0);
251
252 nStatus = STATUS_INVALID_IMAGE_FORMAT;
253
254 /* the buffer doesn't contain the file header */
255 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
256 DIE(("The file doesn't contain the PE file header\n"));
257
258 pinhNtHeader = pData;
259
260 /* object still not aligned: copy it to the beginning of the buffer */
261 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
262 {
263 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
264 RtlMoveMemory(pBuffer, pData, cbReadSize);
265 pinhNtHeader = pBuffer;
266 }
267
268 /* invalid NT header */
269 nStatus = STATUS_INVALID_IMAGE_PROTECT;
270
271 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
272 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
273
274 nStatus = STATUS_INVALID_IMAGE_FORMAT;
275
276 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
277 DIE(("The full NT header is too large\n"));
278
279 /* the buffer doesn't contain the whole NT header */
280 if(cbReadSize < cbNtHeaderSize)
281 DIE(("The file doesn't contain the full NT header\n"));
282 }
283 else
284 {
285 ULONG cbOptHeaderOffsetSize = 0;
286
287 nStatus = STATUS_INVALID_IMAGE_FORMAT;
288
289 /* don't trust an invalid NT header */
290 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
291 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
292
293 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
294 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
295
296 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
297 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
298
299 /* the buffer doesn't contain the whole NT header: read it from the file */
300 if(cbOptHeaderOffsetSize > FileHeaderSize)
301 goto l_ReadHeaderFromFile;
302 }
303
304 /* read information from the NT header */
305 piohOptHeader = &pinhNtHeader->OptionalHeader;
306 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
307
308 nStatus = STATUS_INVALID_IMAGE_FORMAT;
309
310 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
311
312 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
313 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
314
315 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
316
317 switch(piohOptHeader->Magic)
318 {
319 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
320 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
321 break;
322
323 default:
324 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
325 }
326
327 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
328 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
329
330 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
331 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
332 {
333 /* See [1], section 3.4.2 */
334 if(piohOptHeader->SectionAlignment < PAGE_SIZE)
335 {
336 if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
337 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
338 }
339 else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
340 DIE(("The section alignment is smaller than the file alignment\n"));
341
342 nSectionAlignment = piohOptHeader->SectionAlignment;
343 nFileAlignment = piohOptHeader->FileAlignment;
344
345 if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
346 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
347 }
348 else
349 {
350 nSectionAlignment = PAGE_SIZE;
351 nFileAlignment = PAGE_SIZE;
352 }
353
354 ASSERT(IsPowerOf2(nSectionAlignment));
355 ASSERT(IsPowerOf2(nFileAlignment));
356
357 switch(piohOptHeader->Magic)
358 {
359 /* PE32 */
360 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
361 {
362 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
363 ImageSectionObject->ImageBase = piohOptHeader->ImageBase;
364
365 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
366 ImageSectionObject->ImageSize = piohOptHeader->SizeOfImage;
367
368 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
369 ImageSectionObject->StackReserve = piohOptHeader->SizeOfStackReserve;
370
371 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
372 ImageSectionObject->StackCommit = piohOptHeader->SizeOfStackCommit;
373
374 break;
375 }
376
377 /* PE32+ */
378 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
379 {
380 const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
381
382 pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
383
384 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
385 {
386 if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
387 DIE(("ImageBase exceeds the address space\n"));
388
389 ImageSectionObject->ImageBase = pioh64OptHeader->ImageBase;
390 }
391
392 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
393 {
394 if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
395 DIE(("SizeOfImage exceeds the address space\n"));
396
397 ImageSectionObject->ImageSize = pioh64OptHeader->SizeOfImage;
398 }
399
400 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
401 {
402 if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
403 DIE(("SizeOfStackReserve exceeds the address space\n"));
404
405 ImageSectionObject->StackReserve = pioh64OptHeader->SizeOfStackReserve;
406 }
407
408 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
409 {
410 if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
411 DIE(("SizeOfStackCommit exceeds the address space\n"));
412
413 ImageSectionObject->StackCommit = pioh64OptHeader->SizeOfStackCommit;
414 }
415
416 break;
417 }
418 }
419
420 /* [1], section 3.4.2 */
421 if((ULONG_PTR)ImageSectionObject->ImageBase % 0x10000)
422 DIE(("ImageBase is not aligned on a 64KB boundary"));
423
424 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
425 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
426 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
427
428 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
429 {
430 ImageSectionObject->Subsystem = piohOptHeader->Subsystem;
431
432 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
433 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
434 {
435 ImageSectionObject->MinorSubsystemVersion = piohOptHeader->MinorSubsystemVersion;
436 ImageSectionObject->MajorSubsystemVersion = piohOptHeader->MajorSubsystemVersion;
437 }
438 }
439
440 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
441
442 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
443 {
444 ImageSectionObject->EntryPoint = piohOptHeader->ImageBase +
445 piohOptHeader->AddressOfEntryPoint;
446 }
447
448 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
449
450 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
451 ImageSectionObject->Executable = piohOptHeader->SizeOfCode != 0;
452 else
453 ImageSectionObject->Executable = TRUE;
454
455 ImageSectionObject->ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
456 ImageSectionObject->Machine = pinhNtHeader->FileHeader.Machine;
457
458 /* SECTION HEADERS */
459 nStatus = STATUS_INVALID_IMAGE_FORMAT;
460
461 /* see [1], section 3.3 */
462 if(pinhNtHeader->FileHeader.NumberOfSections > 96)
463 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
464
465 /*
466 * the additional segment is for the file's headers. They need to be present for
467 * the benefit of the dynamic loader (to locate exports, defaults for thread
468 * parameters, resources, etc.)
469 */
470 ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
471
472 /* file offset for the section headers */
473 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
474 DIE(("Offset overflow\n"));
475
476 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
477 DIE(("Offset overflow\n"));
478
479 /* size of the section headers */
480 ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
481 cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
482
483 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
484 DIE(("Section headers too large\n"));
485
486 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
487
488 /* size of the executable's headers */
489 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
490 {
491 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
492 // DIE(("SizeOfHeaders is not aligned\n"));
493
494 if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
495 DIE(("The section headers overflow SizeOfHeaders\n"));
496
497 cbHeadersSize = piohOptHeader->SizeOfHeaders;
498 }
499 else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
500 DIE(("Overflow aligning the size of headers\n"));
501
502 if(pBuffer)
503 {
504 ExFreePool(pBuffer);
505 pBuffer = NULL;
506 }
507 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
508 /* WARNING: piohOptHeader IS NO LONGER USABLE */
509 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
510
511 if(FileHeaderSize < cbSectionHeadersOffsetSize)
512 pishSectionHeaders = NULL;
513 else
514 {
515 /*
516 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
517 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
518 */
519 ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
520 pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
521 }
522
523 /*
524 * the buffer doesn't contain the section headers, or the alignment is wrong:
525 * read the headers from the file
526 */
527 if(FileHeaderSize < cbSectionHeadersOffsetSize ||
528 (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
529 {
530 PVOID pData;
531 ULONG cbReadSize;
532
533 lnOffset.QuadPart = cbSectionHeadersOffset;
534
535 /* read the header from the file */
536 nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
537
538 if(!NT_SUCCESS(nStatus))
539 DIE(("ReadFile failed with status %08X\n", nStatus));
540
541 ASSERT(pData);
542 ASSERT(pBuffer);
543 ASSERT(cbReadSize > 0);
544
545 nStatus = STATUS_INVALID_IMAGE_FORMAT;
546
547 /* the buffer doesn't contain all the section headers */
548 if(cbReadSize < cbSectionHeadersSize)
549 DIE(("The file doesn't contain all of the section headers\n"));
550
551 pishSectionHeaders = pData;
552
553 /* object still not aligned: copy it to the beginning of the buffer */
554 if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
555 {
556 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
557 RtlMoveMemory(pBuffer, pData, cbReadSize);
558 pishSectionHeaders = pBuffer;
559 }
560 }
561
562 /* SEGMENTS */
563 /* allocate the segments */
564 nStatus = STATUS_INSUFFICIENT_RESOURCES;
565 ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
566
567 if(ImageSectionObject->Segments == NULL)
568 DIE(("AllocateSegments failed\n"));
569
570 /* initialize the headers segment */
571 pssSegments = ImageSectionObject->Segments;
572
573 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
574
575 if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
576 DIE(("Cannot align the size of the section headers\n"));
577
578 if(!AlignUp(&nPrevVirtualEndOfSegment, cbHeadersSize, nSectionAlignment))
579 DIE(("Cannot align the size of the section headers\n"));
580
581 pssSegments[0].FileOffset = 0;
582 pssSegments[0].Protection = PAGE_READONLY;
583 pssSegments[0].Length = nPrevVirtualEndOfSegment;
584 pssSegments[0].RawLength = nFileSizeOfHeaders;
585 pssSegments[0].VirtualAddress = 0;
586 pssSegments[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA;
587 pssSegments[0].WriteCopy = TRUE;
588
589 /* skip the headers segment */
590 ++ pssSegments;
591
592 nStatus = STATUS_INVALID_IMAGE_FORMAT;
593
594 /* convert the executable sections into segments. See also [1], section 4 */
595 for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
596 {
597 ULONG nCharacteristics;
598
599 /* validate the alignment */
600 if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
601 DIE(("VirtualAddress[%u] is not aligned\n", i));
602
603 /* sections must be contiguous, ordered by base address and non-overlapping */
604 if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
605 DIE(("Memory gap between section %u and the previous\n", i));
606
607 /* ignore explicit BSS sections */
608 if(pishSectionHeaders[i].SizeOfRawData != 0)
609 {
610 /* validate the alignment */
611 #if 0
612 /* Yes, this should be a multiple of FileAlignment, but there's
613 * stuff out there that isn't. We can cope with that
614 */
615 if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
616 DIE(("SizeOfRawData[%u] is not aligned\n", i));
617 #endif
618
619 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
620 // DIE(("PointerToRawData[%u] is not aligned\n", i));
621
622 /* conversion */
623 pssSegments[i].FileOffset = pishSectionHeaders[i].PointerToRawData;
624 pssSegments[i].RawLength = pishSectionHeaders[i].SizeOfRawData;
625 }
626 else
627 {
628 ASSERT(pssSegments[i].FileOffset == 0);
629 ASSERT(pssSegments[i].RawLength == 0);
630 }
631
632 ASSERT(Intsafe_CanAddLong64(pssSegments[i].FileOffset, pssSegments[i].RawLength));
633
634 nCharacteristics = pishSectionHeaders[i].Characteristics;
635
636 /* no explicit protection */
637 if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
638 {
639 if(nCharacteristics & IMAGE_SCN_CNT_CODE)
640 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
641
642 if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
643 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
644
645 if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
646 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
647 }
648
649 /* see table above */
650 pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
651 pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
652
653 if(pishSectionHeaders[i].Misc.VirtualSize == 0 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData)
654 pssSegments[i].Length = pishSectionHeaders[i].SizeOfRawData;
655 else
656 pssSegments[i].Length = pishSectionHeaders[i].Misc.VirtualSize;
657
658 if(!AlignUp(&pssSegments[i].Length, pssSegments[i].Length, nSectionAlignment))
659 DIE(("Cannot align the virtual size of section %u\n", i));
660
661 ASSERT(IsAligned(pssSegments[i].Length, nSectionAlignment));
662
663 if(pssSegments[i].Length == 0)
664 DIE(("Virtual size of section %u is null\n", i));
665
666 pssSegments[i].VirtualAddress = pishSectionHeaders[i].VirtualAddress;
667 pssSegments[i].Characteristics = pishSectionHeaders[i].Characteristics;
668
669 /* ensure the memory image is no larger than 4GB */
670 if(!Intsafe_AddULong32(&nPrevVirtualEndOfSegment, pssSegments[i].VirtualAddress, pssSegments[i].Length))
671 DIE(("The image is larger than 4GB\n"));
672 }
673
674 /* spare our caller some work in validating the segments */
675 *Flags = EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED | EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP;
676
677 if(nSectionAlignment >= PAGE_SIZE)
678 *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
679
680 /* Success */
681 nStatus = STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
682
683 l_Return:
684 if(pBuffer)
685 ExFreePool(pBuffer);
686
687 return nStatus;
688 }
689
690 /* EOF */