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