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
13 /* INCLUDES *****************************************************************/
20 #define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m)))
22 /* FUNCTIONS *****************************************************************/
26 ChkSum(ULONG Sum
, PUSHORT Src
, ULONG Len
)
32 /* Sum up the current word */
35 /* Sum up everything above the low word as a carry */
36 Sum
= (Sum
& 0xFFFF) + (Sum
>> 16);
39 /* Apply carry one more time and clamp to the USHORT */
40 return (Sum
+ (Sum
>> 16)) & 0xFFFF;
45 LdrVerifyMappedImageMatchesChecksum(
51 PIMAGE_NT_HEADERS Header
;
58 // HACK: Ignore calls with ImageSize=0. Should be fixed by new MM.
59 if (ImageSize
== 0) return TRUE
;
61 /* Get NT header to check if it's an image at all */
62 Header
= RtlImageNtHeader(BaseAddress
);
63 if (!Header
) return FALSE
;
65 /* Get checksum to match */
66 HeaderSum
= Header
->OptionalHeader
.CheckSum
;
68 /* Zero checksum seems to be accepted */
69 if (HeaderSum
== 0) return TRUE
;
71 /* Calculate the checksum */
73 Ptr
= (PUSHORT
) BaseAddress
;
74 for (i
= 0; i
< ImageSize
/ sizeof (USHORT
); i
++)
79 Sum
= LOWORD(Sum
) + HIWORD(Sum
);
86 Sum
+= (ULONG
)*((PUCHAR
)Ptr
);
89 Sum
= LOWORD(Sum
) + HIWORD(Sum
);
93 CalcSum
= (USHORT
)(LOWORD(Sum
) + HIWORD(Sum
));
95 /* Subtract image checksum from calculated checksum. */
96 /* fix low word of checksum */
97 if (LOWORD(CalcSum
) >= LOWORD(HeaderSum
))
99 CalcSum
-= LOWORD(HeaderSum
);
103 CalcSum
= ((LOWORD(CalcSum
) - LOWORD(HeaderSum
)) & 0xFFFF) - 1;
106 /* Fix high word of checksum */
107 if (LOWORD(CalcSum
) >= HIWORD(HeaderSum
))
109 CalcSum
-= HIWORD(HeaderSum
);
113 CalcSum
= ((LOWORD(CalcSum
) - HIWORD(HeaderSum
)) & 0xFFFF) - 1;
116 /* Add file length */
117 CalcSum
+= ImageSize
;
119 if (CalcSum
!= HeaderSum
)
120 DPRINT1("Image %p checksum mismatches! 0x%x != 0x%x, ImageSize %x, FileLen %x\n", BaseAddress
, CalcSum
, HeaderSum
, ImageSize
, FileLength
);
122 return (BOOLEAN
)(CalcSum
== HeaderSum
);
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.
144 _Out_ PIMAGE_NT_HEADERS
*OutHeaders
)
146 PIMAGE_NT_HEADERS NtHeaders
;
147 PIMAGE_DOS_HEADER DosHeader
;
148 BOOLEAN WantsRangeCheck
;
149 ULONG NtHeaderOffset
;
151 /* You must want NT Headers, no? */
152 if (OutHeaders
== NULL
)
154 DPRINT1("OutHeaders is NULL\n");
155 return STATUS_INVALID_PARAMETER
;
162 if (Flags
& ~RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK
)
164 DPRINT1("Invalid flags: 0x%lx\n", Flags
);
165 return STATUS_INVALID_PARAMETER
;
169 if ((Base
== NULL
) || (Base
== (PVOID
)-1))
171 DPRINT1("Invalid base address: %p\n", Base
);
172 return STATUS_INVALID_PARAMETER
;
175 /* Check if the caller wants range checks */
176 WantsRangeCheck
= !(Flags
& RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK
);
179 /* Make sure the image size is at least big enough for the DOS header */
180 if (Size
< sizeof(IMAGE_DOS_HEADER
))
182 DPRINT1("Size too small\n");
183 return STATUS_INVALID_IMAGE_FORMAT
;
187 /* Check if the DOS Signature matches */
189 if (DosHeader
->e_magic
!= IMAGE_DOS_SIGNATURE
)
191 /* Not a valid COFF */
192 DPRINT1("Invalid image DOS signature!\n");
193 return STATUS_INVALID_IMAGE_FORMAT
;
196 /* Get the offset to the NT headers (and copy from LONG to ULONG) */
197 NtHeaderOffset
= DosHeader
->e_lfanew
;
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))
206 DPRINT1("NT headers offset is larger than 256MB!\n");
207 return STATUS_INVALID_IMAGE_FORMAT
;
210 /* Check if the caller wants validation */
213 /* Make sure the file header fits into the size */
214 if ((NtHeaderOffset
+
215 RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS
, FileHeader
)) >= Size
)
218 DPRINT1("NT headers beyond image size!\n");
219 return STATUS_INVALID_IMAGE_FORMAT
;
223 /* Now get a pointer to the NT Headers */
224 NtHeaders
= (PIMAGE_NT_HEADERS
)((ULONG_PTR
)Base
+ NtHeaderOffset
);
226 /* Check if the mapping is in user space */
227 if (Base
<= MmHighestUserAddress
)
229 /* Make sure we don't overflow into kernel space */
230 if ((PVOID
)(NtHeaders
+ 1) > MmHighestUserAddress
)
232 DPRINT1("Image overflows from user space into kernel space!\n");
233 return STATUS_INVALID_IMAGE_FORMAT
;
237 /* Verify the PE Signature */
238 if (NtHeaders
->Signature
!= IMAGE_NT_SIGNATURE
)
241 DPRINT1("Invalid image NT signature!\n");
242 return STATUS_INVALID_IMAGE_FORMAT
;
245 /* Now return success and the NT header */
246 *OutHeaders
= NtHeaders
;
247 return STATUS_SUCCESS
;
255 RtlImageNtHeader(IN PVOID Base
)
257 PIMAGE_NT_HEADERS NtHeader
;
259 /* Call the new API */
260 RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK
,
272 RtlImageDirectoryEntryToData(
274 BOOLEAN MappedAsImage
,
278 PIMAGE_NT_HEADERS NtHeader
;
281 /* Magic flag for non-mapped images. */
282 if ((ULONG_PTR
)BaseAddress
& 1)
284 BaseAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
& ~1);
285 MappedAsImage
= FALSE
;
288 NtHeader
= RtlImageNtHeader(BaseAddress
);
289 if (NtHeader
== NULL
)
292 if (Directory
>= SWAPD(NtHeader
->OptionalHeader
.NumberOfRvaAndSizes
))
295 Va
= SWAPD(NtHeader
->OptionalHeader
.DataDirectory
[Directory
].VirtualAddress
);
299 *Size
= SWAPD(NtHeader
->OptionalHeader
.DataDirectory
[Directory
].Size
);
301 if (MappedAsImage
|| Va
< SWAPD(NtHeader
->OptionalHeader
.SizeOfHeaders
))
302 return (PVOID
)((ULONG_PTR
)BaseAddress
+ Va
);
304 /* Image mapped as ordinary file, we must find raw pointer */
305 return RtlImageRvaToVa(NtHeader
, BaseAddress
, Va
, NULL
);
311 PIMAGE_SECTION_HEADER
313 RtlImageRvaToSection(
314 PIMAGE_NT_HEADERS NtHeader
,
318 PIMAGE_SECTION_HEADER Section
;
322 Count
= SWAPW(NtHeader
->FileHeader
.NumberOfSections
);
323 Section
= IMAGE_FIRST_SECTION(NtHeader
);
327 Va
= SWAPD(Section
->VirtualAddress
);
328 if ((Va
<= Rva
) && (Rva
< Va
+ SWAPD(Section
->SizeOfRawData
)))
342 PIMAGE_NT_HEADERS NtHeader
,
345 PIMAGE_SECTION_HEADER
*SectionHeader
)
347 PIMAGE_SECTION_HEADER Section
= NULL
;
350 Section
= *SectionHeader
;
352 if ((Section
== NULL
) ||
353 (Rva
< SWAPD(Section
->VirtualAddress
)) ||
354 (Rva
>= SWAPD(Section
->VirtualAddress
) + SWAPD(Section
->SizeOfRawData
)))
356 Section
= RtlImageRvaToSection(NtHeader
, BaseAddress
, Rva
);
361 *SectionHeader
= Section
;
364 return (PVOID
)((ULONG_PTR
)BaseAddress
+ Rva
+
365 (ULONG_PTR
)SWAPD(Section
->PointerToRawData
) -
366 (ULONG_PTR
)SWAPD(Section
->VirtualAddress
));
369 PIMAGE_BASE_RELOCATION
371 LdrProcessRelocationBlockLongLong(
372 IN ULONG_PTR Address
,
374 IN PUSHORT TypeOffset
,
382 PULONGLONG LongLongPtr
;
384 for (i
= 0; i
< Count
; i
++)
386 Offset
= SWAPW(*TypeOffset
) & 0xFFF;
387 Type
= SWAPW(*TypeOffset
) >> 12;
388 ShortPtr
= (PUSHORT
)(RVA(Address
, Offset
));
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
396 if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir ||
397 (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd)
401 /* case IMAGE_REL_BASED_SECTION : */
402 /* case IMAGE_REL_BASED_REL32 : */
403 case IMAGE_REL_BASED_ABSOLUTE
:
406 case IMAGE_REL_BASED_HIGH
:
407 *ShortPtr
= HIWORD(MAKELONG(0, *ShortPtr
) + (Delta
& 0xFFFFFFFF));
410 case IMAGE_REL_BASED_LOW
:
411 *ShortPtr
= SWAPW(*ShortPtr
) + LOWORD(Delta
& 0xFFFF);
414 case IMAGE_REL_BASED_HIGHLOW
:
415 LongPtr
= (PULONG
)RVA(Address
, Offset
);
416 *LongPtr
= SWAPD(*LongPtr
) + (Delta
& 0xFFFFFFFF);
419 case IMAGE_REL_BASED_DIR64
:
420 LongLongPtr
= (PUINT64
)RVA(Address
, Offset
);
421 *LongLongPtr
= SWAPQ(*LongLongPtr
) + Delta
;
424 case IMAGE_REL_BASED_HIGHADJ
:
425 case IMAGE_REL_BASED_MIPS_JMPADDR
:
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
;
436 return (PIMAGE_BASE_RELOCATION
)TypeOffset
;
442 IN PVOID BaseAddress
,
448 return LdrRelocateImageWithBias(BaseAddress
, 0, LoaderName
, Success
, Conflict
, Invalid
);
453 LdrRelocateImageWithBias(
454 IN PVOID BaseAddress
,
455 IN LONGLONG AdditionalBias
,
461 PIMAGE_NT_HEADERS NtHeaders
;
462 PIMAGE_DATA_DIRECTORY RelocationDDir
;
463 PIMAGE_BASE_RELOCATION RelocationDir
, RelocationEnd
;
469 NtHeaders
= RtlImageNtHeader(BaseAddress
);
471 if (NtHeaders
== NULL
)
474 if (SWAPW(NtHeaders
->FileHeader
.Characteristics
) & IMAGE_FILE_RELOCS_STRIPPED
)
479 RelocationDDir
= &NtHeaders
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_BASERELOC
];
481 if (SWAPD(RelocationDDir
->VirtualAddress
) == 0 || SWAPD(RelocationDDir
->Size
) == 0)
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
));
490 while (RelocationDir
< RelocationEnd
&&
491 SWAPW(RelocationDir
->SizeOfBlock
) > 0)
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);
497 RelocationDir
= LdrProcessRelocationBlockLongLong(Address
,
502 if (RelocationDir
== NULL
)
504 DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n");