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