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