97e8ee86ec8b0e84014503a3c959c6a558beb238
[reactos.git] / reactos / lib / rtl / image.c
1 /* COPYRIGHT: See COPYING in the top level directory
2 * PROJECT: ReactOS system libraries
3 * FILE: lib/rtl/image.c
4 * PURPOSE: Image handling functions
5 * Relocate functions were previously located in
6 * ntoskrnl/ldr/loader.c and
7 * dll/ntdll/ldr/utils.c files
8 * PROGRAMMER: Eric Kohl + original authors from loader.c and utils.c file
9 * Aleksey Bragin
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <rtl.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 #define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m)))
20
21 /* FUNCTIONS *****************************************************************/
22
23 USHORT
24 FORCEINLINE
25 ChkSum(ULONG Sum, PUSHORT Src, ULONG Len)
26 {
27 ULONG i;
28
29 for (i=0; i<Len; i++)
30 {
31 /* Sum up the current word */
32 Sum += Src[i];
33
34 /* Sum up everything above the low word as a carry */
35 Sum = (Sum & 0xFFFF) + (Sum >> 16);
36 }
37
38 /* Apply carry one more time and clamp to the USHORT */
39 return (Sum + (Sum >> 16)) & 0xFFFF;
40 }
41
42 BOOLEAN
43 NTAPI
44 LdrVerifyMappedImageMatchesChecksum(
45 IN PVOID BaseAddress,
46 IN SIZE_T ImageSize,
47 IN ULONG FileLength)
48 {
49 #if 0
50 PIMAGE_NT_HEADERS Header;
51 PUSHORT Ptr;
52 ULONG Sum;
53 ULONG CalcSum;
54 ULONG HeaderSum;
55 ULONG i;
56
57 // HACK: Ignore calls with ImageSize=0. Should be fixed by new MM.
58 if (ImageSize == 0) return TRUE;
59
60 /* Get NT header to check if it's an image at all */
61 Header = RtlImageNtHeader(BaseAddress);
62 if (!Header) return FALSE;
63
64 /* Get checksum to match */
65 HeaderSum = Header->OptionalHeader.CheckSum;
66
67 /* Zero checksum seems to be accepted */
68 if (HeaderSum == 0) return TRUE;
69
70 /* Calculate the checksum */
71 Sum = 0;
72 Ptr = (PUSHORT) BaseAddress;
73 for (i = 0; i < ImageSize / sizeof (USHORT); i++)
74 {
75 Sum += (ULONG)*Ptr;
76 if (HIWORD(Sum) != 0)
77 {
78 Sum = LOWORD(Sum) + HIWORD(Sum);
79 }
80 Ptr++;
81 }
82
83 if (ImageSize & 1)
84 {
85 Sum += (ULONG)*((PUCHAR)Ptr);
86 if (HIWORD(Sum) != 0)
87 {
88 Sum = LOWORD(Sum) + HIWORD(Sum);
89 }
90 }
91
92 CalcSum = (USHORT)(LOWORD(Sum) + HIWORD(Sum));
93
94 /* Subtract image checksum from calculated checksum. */
95 /* fix low word of checksum */
96 if (LOWORD(CalcSum) >= LOWORD(HeaderSum))
97 {
98 CalcSum -= LOWORD(HeaderSum);
99 }
100 else
101 {
102 CalcSum = ((LOWORD(CalcSum) - LOWORD(HeaderSum)) & 0xFFFF) - 1;
103 }
104
105 /* Fix high word of checksum */
106 if (LOWORD(CalcSum) >= HIWORD(HeaderSum))
107 {
108 CalcSum -= HIWORD(HeaderSum);
109 }
110 else
111 {
112 CalcSum = ((LOWORD(CalcSum) - HIWORD(HeaderSum)) & 0xFFFF) - 1;
113 }
114
115 /* Add file length */
116 CalcSum += ImageSize;
117
118 if (CalcSum != HeaderSum)
119 DPRINT1("Image %p checksum mismatches! 0x%x != 0x%x, ImageSize %x, FileLen %x\n", BaseAddress, CalcSum, HeaderSum, ImageSize, FileLength);
120
121 return (BOOLEAN)(CalcSum == HeaderSum);
122 #else
123 /*
124 * FIXME: Warning, this violates the PE standard and makes ReactOS drivers
125 * and other system code when normally on Windows they would not, since
126 * we do not write the checksum in them.
127 * Our compilers should be made to write out the checksum and this function
128 * should be enabled as to reject badly checksummed code.
129 */
130 return TRUE;
131 #endif
132 }
133
134 /*
135 * @implemented
136 */
137 NTSTATUS
138 NTAPI
139 RtlImageNtHeaderEx(IN ULONG Flags,
140 IN PVOID Base,
141 IN ULONG64 Size,
142 OUT PIMAGE_NT_HEADERS *OutHeaders)
143 {
144 PIMAGE_NT_HEADERS NtHeaders;
145 PIMAGE_DOS_HEADER DosHeader;
146 BOOLEAN WantsRangeCheck;
147
148 /* You must want NT Headers, no? */
149 if (!OutHeaders) return STATUS_INVALID_PARAMETER;
150
151 /* Assume failure */
152 *OutHeaders = NULL;
153
154 /* Validate Flags */
155 if (Flags &~ RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK)
156 {
157 DPRINT1("Invalid flag combination... check for new API flags?\n");
158 return STATUS_INVALID_PARAMETER;
159 }
160
161 /* Validate base */
162 if (!(Base) || (Base == (PVOID)-1)) return STATUS_INVALID_PARAMETER;
163
164 /* Check if the caller wants validation */
165 WantsRangeCheck = !(Flags & RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK);
166 if (WantsRangeCheck)
167 {
168 /* Make sure the image size is at least big enough for the DOS header */
169 if (Size < sizeof(IMAGE_DOS_HEADER))
170 {
171 DPRINT1("Size too small\n");
172 return STATUS_INVALID_IMAGE_FORMAT;
173 }
174 }
175
176 /* Check if the DOS Signature matches */
177 DosHeader = Base;
178 if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
179 {
180 /* Not a valid COFF */
181 DPRINT1("Not an MZ file\n");
182 return STATUS_INVALID_IMAGE_FORMAT;
183 }
184
185 /* Check if the caller wants validation */
186 if (WantsRangeCheck)
187 {
188 /* The offset should fit in the passsed-in size */
189 if (DosHeader->e_lfanew >= Size)
190 {
191 /* Fail */
192 DPRINT1("e_lfanew is larger than PE file\n");
193 return STATUS_INVALID_IMAGE_FORMAT;
194 }
195
196 /* It shouldn't be past 4GB either */
197 if (DosHeader->e_lfanew >=
198 (MAXULONG - sizeof(IMAGE_DOS_SIGNATURE) - sizeof(IMAGE_FILE_HEADER)))
199 {
200 /* Fail */
201 DPRINT1("e_lfanew is larger than 4GB\n");
202 return STATUS_INVALID_IMAGE_FORMAT;
203 }
204
205 /* And the whole file shouldn't overflow past 4GB */
206 if ((DosHeader->e_lfanew +
207 sizeof(IMAGE_DOS_SIGNATURE) - sizeof(IMAGE_FILE_HEADER)) >= Size)
208 {
209 /* Fail */
210 DPRINT1("PE is larger than 4GB\n");
211 return STATUS_INVALID_IMAGE_FORMAT;
212 }
213 }
214
215 /* The offset also can't be larger than 256MB, as a hard-coded check */
216 if (DosHeader->e_lfanew >= (256 * 1024 * 1024))
217 {
218 /* Fail */
219 DPRINT1("PE offset is larger than 256MB\n");
220 return STATUS_INVALID_IMAGE_FORMAT;
221 }
222
223 /* Now it's safe to get the NT Headers */
224 NtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)Base + DosHeader->e_lfanew);
225
226 /* Verify the PE Signature */
227 if (NtHeaders->Signature != IMAGE_NT_SIGNATURE)
228 {
229 /* Fail */
230 DPRINT1("PE signature missing\n");
231 return STATUS_INVALID_IMAGE_FORMAT;
232 }
233
234 /* Now return success and the NT header */
235 *OutHeaders = NtHeaders;
236 return STATUS_SUCCESS;
237 }
238
239 /*
240 * @implemented
241 */
242 PIMAGE_NT_HEADERS
243 NTAPI
244 RtlImageNtHeader(IN PVOID Base)
245 {
246 PIMAGE_NT_HEADERS NtHeader;
247
248 /* Call the new API */
249 RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK,
250 Base,
251 0,
252 &NtHeader);
253 return NtHeader;
254 }
255
256 /*
257 * @implemented
258 */
259 PVOID
260 NTAPI
261 RtlImageDirectoryEntryToData(
262 PVOID BaseAddress,
263 BOOLEAN MappedAsImage,
264 USHORT Directory,
265 PULONG Size)
266 {
267 PIMAGE_NT_HEADERS NtHeader;
268 ULONG Va;
269
270 /* Magic flag for non-mapped images. */
271 if ((ULONG_PTR)BaseAddress & 1)
272 {
273 BaseAddress = (PVOID)((ULONG_PTR)BaseAddress & ~1);
274 MappedAsImage = FALSE;
275 }
276
277 NtHeader = RtlImageNtHeader(BaseAddress);
278 if (NtHeader == NULL)
279 return NULL;
280
281 if (Directory >= SWAPD(NtHeader->OptionalHeader.NumberOfRvaAndSizes))
282 return NULL;
283
284 Va = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].VirtualAddress);
285 if (Va == 0)
286 return NULL;
287
288 *Size = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].Size);
289
290 if (MappedAsImage || Va < SWAPD(NtHeader->OptionalHeader.SizeOfHeaders))
291 return (PVOID)((ULONG_PTR)BaseAddress + Va);
292
293 /* image mapped as ordinary file, we must find raw pointer */
294 return RtlImageRvaToVa(NtHeader, BaseAddress, Va, NULL);
295 }
296
297
298 /*
299 * @implemented
300 */
301 PIMAGE_SECTION_HEADER
302 NTAPI
303 RtlImageRvaToSection(
304 PIMAGE_NT_HEADERS NtHeader,
305 PVOID BaseAddress,
306 ULONG Rva)
307 {
308 PIMAGE_SECTION_HEADER Section;
309 ULONG Va;
310 ULONG Count;
311
312 Count = SWAPW(NtHeader->FileHeader.NumberOfSections);
313 Section = IMAGE_FIRST_SECTION(NtHeader);
314
315 while (Count--)
316 {
317 Va = SWAPD(Section->VirtualAddress);
318 if ((Va <= Rva) &&
319 (Rva < Va + SWAPD(Section->Misc.VirtualSize)))
320 return Section;
321 Section++;
322 }
323 return NULL;
324 }
325
326
327 /*
328 * @implemented
329 */
330 PVOID
331 NTAPI
332 RtlImageRvaToVa(
333 PIMAGE_NT_HEADERS NtHeader,
334 PVOID BaseAddress,
335 ULONG Rva,
336 PIMAGE_SECTION_HEADER *SectionHeader)
337 {
338 PIMAGE_SECTION_HEADER Section = NULL;
339
340 if (SectionHeader)
341 Section = *SectionHeader;
342
343 if ((Section == NULL) ||
344 (Rva < SWAPD(Section->VirtualAddress)) ||
345 (Rva >= SWAPD(Section->VirtualAddress) + SWAPD(Section->Misc.VirtualSize)))
346 {
347 Section = RtlImageRvaToSection (NtHeader, BaseAddress, Rva);
348 if (Section == NULL)
349 return NULL;
350
351 if (SectionHeader)
352 *SectionHeader = Section;
353 }
354
355 return (PVOID)((ULONG_PTR)BaseAddress +
356 Rva +
357 SWAPD(Section->PointerToRawData) -
358 (ULONG_PTR)SWAPD(Section->VirtualAddress));
359 }
360
361 PIMAGE_BASE_RELOCATION
362 NTAPI
363 LdrProcessRelocationBlockLongLong(
364 IN ULONG_PTR Address,
365 IN ULONG Count,
366 IN PUSHORT TypeOffset,
367 IN LONGLONG Delta)
368 {
369 SHORT Offset;
370 USHORT Type;
371 ULONG i;
372 PUSHORT ShortPtr;
373 PULONG LongPtr;
374 PULONGLONG LongLongPtr;
375
376 for (i = 0; i < Count; i++)
377 {
378 Offset = SWAPW(*TypeOffset) & 0xFFF;
379 Type = SWAPW(*TypeOffset) >> 12;
380 ShortPtr = (PUSHORT)(RVA(Address, Offset));
381 /*
382 * Don't relocate within the relocation section itself.
383 * GCC/LD generates sometimes relocation records for the relocation section.
384 * This is a bug in GCC/LD.
385 * Fix for it disabled, since it was only in ntoskrnl and not in ntdll
386 */
387 /*
388 if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir ||
389 (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd)
390 {*/
391 switch (Type)
392 {
393 /* case IMAGE_REL_BASED_SECTION : */
394 /* case IMAGE_REL_BASED_REL32 : */
395 case IMAGE_REL_BASED_ABSOLUTE:
396 break;
397
398 case IMAGE_REL_BASED_HIGH:
399 *ShortPtr = HIWORD(MAKELONG(0, *ShortPtr) + (Delta & 0xFFFFFFFF));
400 break;
401
402 case IMAGE_REL_BASED_LOW:
403 *ShortPtr = SWAPW(*ShortPtr) + LOWORD(Delta & 0xFFFF);
404 break;
405
406 case IMAGE_REL_BASED_HIGHLOW:
407 LongPtr = (PULONG)RVA(Address, Offset);
408 *LongPtr = SWAPD(*LongPtr) + (Delta & 0xFFFFFFFF);
409 break;
410
411 case IMAGE_REL_BASED_DIR64:
412 LongLongPtr = (PUINT64)RVA(Address, Offset);
413 *LongLongPtr = SWAPQ(*LongLongPtr) + Delta;
414 break;
415
416 case IMAGE_REL_BASED_HIGHADJ:
417 case IMAGE_REL_BASED_MIPS_JMPADDR:
418 default:
419 DPRINT1("Unknown/unsupported fixup type %hu.\n", Type);
420 DPRINT1("Address %p, Current %u, Count %u, *TypeOffset %x\n",
421 (PVOID)Address, i, Count, SWAPW(*TypeOffset));
422 return (PIMAGE_BASE_RELOCATION)NULL;
423 }
424
425 TypeOffset++;
426 }
427
428 return (PIMAGE_BASE_RELOCATION)TypeOffset;
429 }
430
431 ULONG
432 NTAPI
433 LdrRelocateImageWithBias(
434 IN PVOID BaseAddress,
435 IN LONGLONG AdditionalBias,
436 IN PCCH LoaderName,
437 IN ULONG Success,
438 IN ULONG Conflict,
439 IN ULONG Invalid)
440 {
441 PIMAGE_NT_HEADERS NtHeaders;
442 PIMAGE_DATA_DIRECTORY RelocationDDir;
443 PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd;
444 ULONG Count;
445 ULONG_PTR Address;
446 PUSHORT TypeOffset;
447 LONGLONG Delta;
448
449 NtHeaders = RtlImageNtHeader(BaseAddress);
450
451 if (NtHeaders == NULL)
452 return Invalid;
453
454 if (SWAPW(NtHeaders->FileHeader.Characteristics) & IMAGE_FILE_RELOCS_STRIPPED)
455 {
456 return Conflict;
457 }
458
459 RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
460
461 if (SWAPD(RelocationDDir->VirtualAddress) == 0 || SWAPD(RelocationDDir->Size) == 0)
462 {
463 return Success;
464 }
465
466 Delta = (ULONG_PTR)BaseAddress - SWAPD(NtHeaders->OptionalHeader.ImageBase) + AdditionalBias;
467 RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseAddress + SWAPD(RelocationDDir->VirtualAddress));
468 RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + SWAPD(RelocationDDir->Size));
469
470 while (RelocationDir < RelocationEnd &&
471 SWAPW(RelocationDir->SizeOfBlock) > 0)
472 {
473 Count = (SWAPW(RelocationDir->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
474 Address = (ULONG_PTR)RVA(BaseAddress, SWAPD(RelocationDir->VirtualAddress));
475 TypeOffset = (PUSHORT)(RelocationDir + 1);
476
477 RelocationDir = LdrProcessRelocationBlockLongLong(Address,
478 Count,
479 TypeOffset,
480 Delta);
481
482 if (RelocationDir == NULL)
483 {
484 DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n");
485 return Invalid;
486 }
487 }
488
489 return Success;
490 }
491
492 /* EOF */