[RTL] Fix amd64 version of DebugService2
[reactos.git] / sdk / lib / rtl / image.c
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 */