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
12 /* INCLUDES *****************************************************************/
19 #define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m)))
21 /* FUNCTIONS *****************************************************************/
25 ChkSum(ULONG Sum
, PUSHORT Src
, ULONG Len
)
31 /* Sum up the current word */
34 /* Sum up everything above the low word as a carry */
35 Sum
= (Sum
& 0xFFFF) + (Sum
>> 16);
38 /* Apply carry one more time and clamp to the USHORT */
39 return (Sum
+ (Sum
>> 16)) & 0xFFFF;
44 LdrVerifyMappedImageMatchesChecksum(
50 PIMAGE_NT_HEADERS Header
;
57 // HACK: Ignore calls with ImageSize=0. Should be fixed by new MM.
58 if (ImageSize
== 0) return TRUE
;
60 /* Get NT header to check if it's an image at all */
61 Header
= RtlImageNtHeader(BaseAddress
);
62 if (!Header
) return FALSE
;
64 /* Get checksum to match */
65 HeaderSum
= Header
->OptionalHeader
.CheckSum
;
67 /* Zero checksum seems to be accepted */
68 if (HeaderSum
== 0) return TRUE
;
70 /* Calculate the checksum */
72 Ptr
= (PUSHORT
) BaseAddress
;
73 for (i
= 0; i
< ImageSize
/ sizeof (USHORT
); i
++)
78 Sum
= LOWORD(Sum
) + HIWORD(Sum
);
85 Sum
+= (ULONG
)*((PUCHAR
)Ptr
);
88 Sum
= LOWORD(Sum
) + HIWORD(Sum
);
92 CalcSum
= (USHORT
)(LOWORD(Sum
) + HIWORD(Sum
));
94 /* Subtract image checksum from calculated checksum. */
95 /* fix low word of checksum */
96 if (LOWORD(CalcSum
) >= LOWORD(HeaderSum
))
98 CalcSum
-= LOWORD(HeaderSum
);
102 CalcSum
= ((LOWORD(CalcSum
) - LOWORD(HeaderSum
)) & 0xFFFF) - 1;
105 /* Fix high word of checksum */
106 if (LOWORD(CalcSum
) >= HIWORD(HeaderSum
))
108 CalcSum
-= HIWORD(HeaderSum
);
112 CalcSum
= ((LOWORD(CalcSum
) - HIWORD(HeaderSum
)) & 0xFFFF) - 1;
115 /* Add file length */
116 CalcSum
+= ImageSize
;
118 if (CalcSum
!= HeaderSum
)
119 DPRINT1("Image %p checksum mismatches! 0x%x != 0x%x, ImageSize %x, FileLen %x\n", BaseAddress
, CalcSum
, HeaderSum
, ImageSize
, FileLength
);
121 return (BOOLEAN
)(CalcSum
== HeaderSum
);
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.
139 RtlImageNtHeaderEx(IN ULONG Flags
,
142 OUT PIMAGE_NT_HEADERS
*OutHeaders
)
144 PIMAGE_NT_HEADERS NtHeaders
;
145 PIMAGE_DOS_HEADER DosHeader
;
146 BOOLEAN WantsRangeCheck
;
148 /* You must want NT Headers, no? */
149 if (!OutHeaders
) return STATUS_INVALID_PARAMETER
;
155 if (Flags
&~ RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK
)
157 DPRINT1("Invalid flag combination... check for new API flags?\n");
158 return STATUS_INVALID_PARAMETER
;
162 if (!(Base
) || (Base
== (PVOID
)-1)) return STATUS_INVALID_PARAMETER
;
164 /* Check if the caller wants validation */
165 WantsRangeCheck
= !(Flags
& RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK
);
168 /* Make sure the image size is at least big enough for the DOS header */
169 if (Size
< sizeof(IMAGE_DOS_HEADER
))
171 DPRINT1("Size too small\n");
172 return STATUS_INVALID_IMAGE_FORMAT
;
176 /* Check if the DOS Signature matches */
178 if (DosHeader
->e_magic
!= IMAGE_DOS_SIGNATURE
)
180 /* Not a valid COFF */
181 DPRINT1("Not an MZ file\n");
182 return STATUS_INVALID_IMAGE_FORMAT
;
185 /* Check if the caller wants validation */
188 /* The offset should fit in the passsed-in size */
189 if (DosHeader
->e_lfanew
>= Size
)
192 DPRINT1("e_lfanew is larger than PE file\n");
193 return STATUS_INVALID_IMAGE_FORMAT
;
196 /* It shouldn't be past 4GB either */
197 if (DosHeader
->e_lfanew
>=
198 (MAXULONG
- sizeof(IMAGE_DOS_SIGNATURE
) - sizeof(IMAGE_FILE_HEADER
)))
201 DPRINT1("e_lfanew is larger than 4GB\n");
202 return STATUS_INVALID_IMAGE_FORMAT
;
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
)
210 DPRINT1("PE is larger than 4GB\n");
211 return STATUS_INVALID_IMAGE_FORMAT
;
215 /* The offset also can't be larger than 256MB, as a hard-coded check */
216 if (DosHeader
->e_lfanew
>= (256 * 1024 * 1024))
219 DPRINT1("PE offset is larger than 256MB\n");
220 return STATUS_INVALID_IMAGE_FORMAT
;
223 /* Now it's safe to get the NT Headers */
224 NtHeaders
= (PIMAGE_NT_HEADERS
)((ULONG_PTR
)Base
+ DosHeader
->e_lfanew
);
226 /* Verify the PE Signature */
227 if (NtHeaders
->Signature
!= IMAGE_NT_SIGNATURE
)
230 DPRINT1("PE signature missing\n");
231 return STATUS_INVALID_IMAGE_FORMAT
;
234 /* Now return success and the NT header */
235 *OutHeaders
= NtHeaders
;
236 return STATUS_SUCCESS
;
244 RtlImageNtHeader(IN PVOID Base
)
246 PIMAGE_NT_HEADERS NtHeader
;
248 /* Call the new API */
249 RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK
,
261 RtlImageDirectoryEntryToData(
263 BOOLEAN MappedAsImage
,
267 PIMAGE_NT_HEADERS NtHeader
;
270 /* Magic flag for non-mapped images. */
271 if ((ULONG_PTR
)BaseAddress
& 1)
273 BaseAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
& ~1);
274 MappedAsImage
= FALSE
;
277 NtHeader
= RtlImageNtHeader(BaseAddress
);
278 if (NtHeader
== NULL
)
281 if (Directory
>= SWAPD(NtHeader
->OptionalHeader
.NumberOfRvaAndSizes
))
284 Va
= SWAPD(NtHeader
->OptionalHeader
.DataDirectory
[Directory
].VirtualAddress
);
288 *Size
= SWAPD(NtHeader
->OptionalHeader
.DataDirectory
[Directory
].Size
);
290 if (MappedAsImage
|| Va
< SWAPD(NtHeader
->OptionalHeader
.SizeOfHeaders
))
291 return (PVOID
)((ULONG_PTR
)BaseAddress
+ Va
);
293 /* image mapped as ordinary file, we must find raw pointer */
294 return RtlImageRvaToVa(NtHeader
, BaseAddress
, Va
, NULL
);
301 PIMAGE_SECTION_HEADER
303 RtlImageRvaToSection(
304 PIMAGE_NT_HEADERS NtHeader
,
308 PIMAGE_SECTION_HEADER Section
;
312 Count
= SWAPW(NtHeader
->FileHeader
.NumberOfSections
);
313 Section
= IMAGE_FIRST_SECTION(NtHeader
);
317 Va
= SWAPD(Section
->VirtualAddress
);
319 (Rva
< Va
+ SWAPD(Section
->Misc
.VirtualSize
)))
333 PIMAGE_NT_HEADERS NtHeader
,
336 PIMAGE_SECTION_HEADER
*SectionHeader
)
338 PIMAGE_SECTION_HEADER Section
= NULL
;
341 Section
= *SectionHeader
;
343 if ((Section
== NULL
) ||
344 (Rva
< SWAPD(Section
->VirtualAddress
)) ||
345 (Rva
>= SWAPD(Section
->VirtualAddress
) + SWAPD(Section
->Misc
.VirtualSize
)))
347 Section
= RtlImageRvaToSection (NtHeader
, BaseAddress
, Rva
);
352 *SectionHeader
= Section
;
355 return (PVOID
)((ULONG_PTR
)BaseAddress
+
357 SWAPD(Section
->PointerToRawData
) -
358 (ULONG_PTR
)SWAPD(Section
->VirtualAddress
));
361 PIMAGE_BASE_RELOCATION
363 LdrProcessRelocationBlockLongLong(
364 IN ULONG_PTR Address
,
366 IN PUSHORT TypeOffset
,
374 PULONGLONG LongLongPtr
;
376 for (i
= 0; i
< Count
; i
++)
378 Offset
= SWAPW(*TypeOffset
) & 0xFFF;
379 Type
= SWAPW(*TypeOffset
) >> 12;
380 ShortPtr
= (PUSHORT
)(RVA(Address
, Offset
));
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
388 if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir ||
389 (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd)
393 /* case IMAGE_REL_BASED_SECTION : */
394 /* case IMAGE_REL_BASED_REL32 : */
395 case IMAGE_REL_BASED_ABSOLUTE
:
398 case IMAGE_REL_BASED_HIGH
:
399 *ShortPtr
= HIWORD(MAKELONG(0, *ShortPtr
) + (Delta
& 0xFFFFFFFF));
402 case IMAGE_REL_BASED_LOW
:
403 *ShortPtr
= SWAPW(*ShortPtr
) + LOWORD(Delta
& 0xFFFF);
406 case IMAGE_REL_BASED_HIGHLOW
:
407 LongPtr
= (PULONG
)RVA(Address
, Offset
);
408 *LongPtr
= SWAPD(*LongPtr
) + (Delta
& 0xFFFFFFFF);
411 case IMAGE_REL_BASED_DIR64
:
412 LongLongPtr
= (PUINT64
)RVA(Address
, Offset
);
413 *LongLongPtr
= SWAPQ(*LongLongPtr
) + Delta
;
416 case IMAGE_REL_BASED_HIGHADJ
:
417 case IMAGE_REL_BASED_MIPS_JMPADDR
:
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
;
428 return (PIMAGE_BASE_RELOCATION
)TypeOffset
;
433 LdrRelocateImageWithBias(
434 IN PVOID BaseAddress
,
435 IN LONGLONG AdditionalBias
,
441 PIMAGE_NT_HEADERS NtHeaders
;
442 PIMAGE_DATA_DIRECTORY RelocationDDir
;
443 PIMAGE_BASE_RELOCATION RelocationDir
, RelocationEnd
;
449 NtHeaders
= RtlImageNtHeader(BaseAddress
);
451 if (NtHeaders
== NULL
)
454 if (SWAPW(NtHeaders
->FileHeader
.Characteristics
) & IMAGE_FILE_RELOCS_STRIPPED
)
459 RelocationDDir
= &NtHeaders
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_BASERELOC
];
461 if (SWAPD(RelocationDDir
->VirtualAddress
) == 0 || SWAPD(RelocationDDir
->Size
) == 0)
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
));
470 while (RelocationDir
< RelocationEnd
&&
471 SWAPW(RelocationDir
->SizeOfBlock
) > 0)
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);
477 RelocationDir
= LdrProcessRelocationBlockLongLong(Address
,
482 if (RelocationDir
== NULL
)
484 DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n");