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