xref: /reactos/sdk/lib/rtl/image.c (revision c2c66aff)
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  */
138 NTSTATUS
139 NTAPI
140 RtlImageNtHeaderEx(
141     _In_ ULONG Flags,
142     _In_ PVOID Base,
143     _In_ ULONG64 Size,
144     _Out_ PIMAGE_NT_HEADERS *OutHeaders)
145 {
146     PIMAGE_NT_HEADERS NtHeaders;
147     PIMAGE_DOS_HEADER DosHeader;
148     BOOLEAN WantsRangeCheck;
149     ULONG NtHeaderOffset;
150 
151     /* You must want NT Headers, no? */
152     if (OutHeaders == NULL)
153     {
154         DPRINT1("OutHeaders is NULL\n");
155         return STATUS_INVALID_PARAMETER;
156     }
157 
158     /* Assume failure */
159     *OutHeaders = NULL;
160 
161     /* Validate Flags */
162     if (Flags & ~RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK)
163     {
164         DPRINT1("Invalid flags: 0x%lx\n", Flags);
165         return STATUS_INVALID_PARAMETER;
166     }
167 
168     /* Validate base */
169     if ((Base == NULL) || (Base == (PVOID)-1))
170     {
171         DPRINT1("Invalid base address: %p\n", Base);
172         return STATUS_INVALID_PARAMETER;
173     }
174 
175     /* Check if the caller wants range checks */
176     WantsRangeCheck = !(Flags & RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK);
177     if (WantsRangeCheck)
178     {
179         /* Make sure the image size is at least big enough for the DOS header */
180         if (Size < sizeof(IMAGE_DOS_HEADER))
181         {
182             DPRINT1("Size too small\n");
183             return STATUS_INVALID_IMAGE_FORMAT;
184         }
185     }
186 
187     /* Check if the DOS Signature matches */
188     DosHeader = Base;
189     if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
190     {
191         /* Not a valid COFF */
192         DPRINT1("Invalid image DOS signature!\n");
193         return STATUS_INVALID_IMAGE_FORMAT;
194     }
195 
196     /* Get the offset to the NT headers (and copy from LONG to ULONG) */
197     NtHeaderOffset = DosHeader->e_lfanew;
198 
199     /* The offset must not be larger than 256MB, as a hard-coded check.
200        In Windows this check is only done in user mode, not in kernel mode,
201        but it shouldn't harm to have it anyway. Note that without this check,
202        other overflow checks would become necessary! */
203     if (NtHeaderOffset >= (256 * 1024 * 1024))
204     {
205         /* Fail */
206         DPRINT1("NT headers offset is larger than 256MB!\n");
207         return STATUS_INVALID_IMAGE_FORMAT;
208     }
209 
210     /* Check if the caller wants validation */
211     if (WantsRangeCheck)
212     {
213         /* Make sure the file header fits into the size */
214         if ((NtHeaderOffset +
215              RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS, FileHeader)) >= Size)
216         {
217             /* Fail */
218             DPRINT1("NT headers beyond image size!\n");
219             return STATUS_INVALID_IMAGE_FORMAT;
220         }
221     }
222 
223     /* Now get a pointer to the NT Headers */
224     NtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)Base + NtHeaderOffset);
225 
226     /* Check if the mapping is in user space */
227     if (Base <= MmHighestUserAddress)
228     {
229         /* Make sure we don't overflow into kernel space */
230         if ((PVOID)(NtHeaders + 1) > MmHighestUserAddress)
231         {
232             DPRINT1("Image overflows from user space into kernel space!\n");
233             return STATUS_INVALID_IMAGE_FORMAT;
234         }
235     }
236 
237     /* Verify the PE Signature */
238     if (NtHeaders->Signature != IMAGE_NT_SIGNATURE)
239     {
240         /* Fail */
241         DPRINT1("Invalid image NT signature!\n");
242         return STATUS_INVALID_IMAGE_FORMAT;
243     }
244 
245     /* Now return success and the NT header */
246     *OutHeaders = NtHeaders;
247     return STATUS_SUCCESS;
248 }
249 
250 /*
251  * @implemented
252  */
253 PIMAGE_NT_HEADERS
254 NTAPI
255 RtlImageNtHeader(IN PVOID Base)
256 {
257     PIMAGE_NT_HEADERS NtHeader;
258 
259     /* Call the new API */
260     RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK,
261                        Base,
262                        0,
263                        &NtHeader);
264     return NtHeader;
265 }
266 
267 /*
268  * @implemented
269  */
270 PVOID
271 NTAPI
272 RtlImageDirectoryEntryToData(
273     PVOID BaseAddress,
274     BOOLEAN MappedAsImage,
275     USHORT Directory,
276     PULONG Size)
277 {
278     PIMAGE_NT_HEADERS NtHeader;
279     ULONG Va;
280 
281     /* Magic flag for non-mapped images. */
282     if ((ULONG_PTR)BaseAddress & 1)
283     {
284         BaseAddress = (PVOID)((ULONG_PTR)BaseAddress & ~1);
285         MappedAsImage = FALSE;
286     }
287 
288     NtHeader = RtlImageNtHeader(BaseAddress);
289     if (NtHeader == NULL)
290         return NULL;
291 
292     if (Directory >= SWAPD(NtHeader->OptionalHeader.NumberOfRvaAndSizes))
293         return NULL;
294 
295     Va = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].VirtualAddress);
296     if (Va == 0)
297         return NULL;
298 
299     *Size = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].Size);
300 
301     if (MappedAsImage || Va < SWAPD(NtHeader->OptionalHeader.SizeOfHeaders))
302         return (PVOID)((ULONG_PTR)BaseAddress + Va);
303 
304     /* Image mapped as ordinary file, we must find raw pointer */
305     return RtlImageRvaToVa(NtHeader, BaseAddress, Va, NULL);
306 }
307 
308 /*
309  * @implemented
310  */
311 PIMAGE_SECTION_HEADER
312 NTAPI
313 RtlImageRvaToSection(
314     PIMAGE_NT_HEADERS NtHeader,
315     PVOID BaseAddress,
316     ULONG Rva)
317 {
318     PIMAGE_SECTION_HEADER Section;
319     ULONG Va;
320     ULONG Count;
321 
322     Count = SWAPW(NtHeader->FileHeader.NumberOfSections);
323     Section = IMAGE_FIRST_SECTION(NtHeader);
324 
325     while (Count--)
326     {
327         Va = SWAPD(Section->VirtualAddress);
328         if ((Va <= Rva) && (Rva < Va + SWAPD(Section->SizeOfRawData)))
329             return Section;
330         Section++;
331     }
332 
333     return NULL;
334 }
335 
336 /*
337  * @implemented
338  */
339 PVOID
340 NTAPI
341 RtlImageRvaToVa(
342     PIMAGE_NT_HEADERS NtHeader,
343     PVOID BaseAddress,
344     ULONG Rva,
345     PIMAGE_SECTION_HEADER *SectionHeader)
346 {
347     PIMAGE_SECTION_HEADER Section = NULL;
348 
349     if (SectionHeader)
350         Section = *SectionHeader;
351 
352     if ((Section == NULL) ||
353         (Rva < SWAPD(Section->VirtualAddress)) ||
354         (Rva >= SWAPD(Section->VirtualAddress) + SWAPD(Section->SizeOfRawData)))
355     {
356         Section = RtlImageRvaToSection(NtHeader, BaseAddress, Rva);
357         if (Section == NULL)
358             return NULL;
359 
360         if (SectionHeader)
361             *SectionHeader = Section;
362     }
363 
364     return (PVOID)((ULONG_PTR)BaseAddress + Rva +
365                    (ULONG_PTR)SWAPD(Section->PointerToRawData) -
366                    (ULONG_PTR)SWAPD(Section->VirtualAddress));
367 }
368 
369 PIMAGE_BASE_RELOCATION
370 NTAPI
371 LdrProcessRelocationBlockLongLong(
372     IN ULONG_PTR Address,
373     IN ULONG Count,
374     IN PUSHORT TypeOffset,
375     IN LONGLONG Delta)
376 {
377     SHORT Offset;
378     USHORT Type;
379     ULONG i;
380     PUSHORT ShortPtr;
381     PULONG LongPtr;
382     PULONGLONG LongLongPtr;
383 
384     for (i = 0; i < Count; i++)
385     {
386         Offset = SWAPW(*TypeOffset) & 0xFFF;
387         Type = SWAPW(*TypeOffset) >> 12;
388         ShortPtr = (PUSHORT)(RVA(Address, Offset));
389         /*
390         * Don't relocate within the relocation section itself.
391         * GCC/LD generates sometimes relocation records for the relocation section.
392         * This is a bug in GCC/LD.
393         * Fix for it disabled, since it was only in ntoskrnl and not in ntdll
394         */
395         /*
396         if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir ||
397         (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd)
398         {*/
399         switch (Type)
400         {
401             /* case IMAGE_REL_BASED_SECTION : */
402             /* case IMAGE_REL_BASED_REL32 : */
403         case IMAGE_REL_BASED_ABSOLUTE:
404             break;
405 
406         case IMAGE_REL_BASED_HIGH:
407             *ShortPtr = HIWORD(MAKELONG(0, *ShortPtr) + (Delta & 0xFFFFFFFF));
408             break;
409 
410         case IMAGE_REL_BASED_LOW:
411             *ShortPtr = SWAPW(*ShortPtr) + LOWORD(Delta & 0xFFFF);
412             break;
413 
414         case IMAGE_REL_BASED_HIGHLOW:
415             LongPtr = (PULONG)RVA(Address, Offset);
416             *LongPtr = SWAPD(*LongPtr) + (Delta & 0xFFFFFFFF);
417             break;
418 
419         case IMAGE_REL_BASED_DIR64:
420             LongLongPtr = (PUINT64)RVA(Address, Offset);
421             *LongLongPtr = SWAPQ(*LongLongPtr) + Delta;
422             break;
423 
424         case IMAGE_REL_BASED_HIGHADJ:
425         case IMAGE_REL_BASED_MIPS_JMPADDR:
426         default:
427             DPRINT1("Unknown/unsupported fixup type %hu.\n", Type);
428             DPRINT1("Address %p, Current %u, Count %u, *TypeOffset %x\n",
429                     (PVOID)Address, i, Count, SWAPW(*TypeOffset));
430             return (PIMAGE_BASE_RELOCATION)NULL;
431         }
432 
433         TypeOffset++;
434     }
435 
436     return (PIMAGE_BASE_RELOCATION)TypeOffset;
437 }
438 
439 ULONG
440 NTAPI
441 LdrRelocateImage(
442     IN PVOID BaseAddress,
443     IN PCCH  LoaderName,
444     IN ULONG Success,
445     IN ULONG Conflict,
446     IN ULONG Invalid)
447 {
448     return LdrRelocateImageWithBias(BaseAddress, 0, LoaderName, Success, Conflict, Invalid);
449 }
450 
451 ULONG
452 NTAPI
453 LdrRelocateImageWithBias(
454     IN PVOID BaseAddress,
455     IN LONGLONG AdditionalBias,
456     IN PCCH  LoaderName,
457     IN ULONG Success,
458     IN ULONG Conflict,
459     IN ULONG Invalid)
460 {
461     PIMAGE_NT_HEADERS NtHeaders;
462     PIMAGE_DATA_DIRECTORY RelocationDDir;
463     PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd;
464     ULONG Count;
465     ULONG_PTR Address;
466     PUSHORT TypeOffset;
467     LONGLONG Delta;
468 
469     NtHeaders = RtlImageNtHeader(BaseAddress);
470 
471     if (NtHeaders == NULL)
472         return Invalid;
473 
474     if (SWAPW(NtHeaders->FileHeader.Characteristics) & IMAGE_FILE_RELOCS_STRIPPED)
475     {
476         return Conflict;
477     }
478 
479     RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
480 
481     if (SWAPD(RelocationDDir->VirtualAddress) == 0 || SWAPD(RelocationDDir->Size) == 0)
482     {
483         return Success;
484     }
485 
486     Delta = (ULONG_PTR)BaseAddress - SWAPD(NtHeaders->OptionalHeader.ImageBase) + AdditionalBias;
487     RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseAddress + SWAPD(RelocationDDir->VirtualAddress));
488     RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + SWAPD(RelocationDDir->Size));
489 
490     while (RelocationDir < RelocationEnd &&
491             SWAPW(RelocationDir->SizeOfBlock) > 0)
492     {
493         Count = (SWAPW(RelocationDir->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
494         Address = (ULONG_PTR)RVA(BaseAddress, SWAPD(RelocationDir->VirtualAddress));
495         TypeOffset = (PUSHORT)(RelocationDir + 1);
496 
497         RelocationDir = LdrProcessRelocationBlockLongLong(Address,
498                         Count,
499                         TypeOffset,
500                         Delta);
501 
502         if (RelocationDir == NULL)
503         {
504             DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n");
505             return Invalid;
506         }
507     }
508 
509     return Success;
510 }
511 
512 /* EOF */
513