[RTL]
[reactos.git] / reactos / lib / rtl / image.c
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
9 * Aleksey Bragin
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <rtl.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 #define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m)))
20
21 /* FUNCTIONS *****************************************************************/
22
23 USHORT
24 FORCEINLINE
25 ChkSum(ULONG Sum, PUSHORT Src, ULONG Len)
26 {
27 ULONG i;
28
29 for (i=0; i<Len; i++)
30 {
31 /* Sum up the current word */
32 Sum += Src[i];
33
34 /* Sum up everything above the low word as a carry */
35 Sum = (Sum & 0xFFFF) + (Sum >> 16);
36 }
37
38 /* Apply carry one more time and clamp to the USHORT */
39 return (Sum + (Sum >> 16)) & 0xFFFF;
40 }
41
42 BOOLEAN
43 NTAPI
44 LdrVerifyMappedImageMatchesChecksum(
45 IN PVOID BaseAddress,
46 IN ULONG ImageSize,
47 IN ULONG FileLength)
48 {
49 PIMAGE_NT_HEADERS Header;
50 PUSHORT Ptr;
51 ULONG Sum;
52 ULONG CalcSum;
53 ULONG HeaderSum;
54 ULONG i;
55
56 // HACK: Ignore calls with ImageSize=0. Should be fixed by new MM.
57 if (ImageSize == 0) return TRUE;
58
59 /* Get NT header to check if it's an image at all */
60 Header = RtlImageNtHeader(BaseAddress);
61 if (!Header) return FALSE;
62
63 /* Get checksum to match */
64 HeaderSum = Header->OptionalHeader.CheckSum;
65
66 /* Zero checksum seems to be accepted */
67 if (HeaderSum == 0) return TRUE;
68
69 /* Calculate the checksum */
70 Sum = 0;
71 Ptr = (PUSHORT) BaseAddress;
72 for (i = 0; i < ImageSize / sizeof (USHORT); i++)
73 {
74 Sum += (ULONG)*Ptr;
75 if (HIWORD(Sum) != 0)
76 {
77 Sum = LOWORD(Sum) + HIWORD(Sum);
78 }
79 Ptr++;
80 }
81
82 if (ImageSize & 1)
83 {
84 Sum += (ULONG)*((PUCHAR)Ptr);
85 if (HIWORD(Sum) != 0)
86 {
87 Sum = LOWORD(Sum) + HIWORD(Sum);
88 }
89 }
90
91 CalcSum = (USHORT)(LOWORD(Sum) + HIWORD(Sum));
92
93 /* Subtract image checksum from calculated checksum. */
94 /* fix low word of checksum */
95 if (LOWORD(CalcSum) >= LOWORD(HeaderSum))
96 {
97 CalcSum -= LOWORD(HeaderSum);
98 }
99 else
100 {
101 CalcSum = ((LOWORD(CalcSum) - LOWORD(HeaderSum)) & 0xFFFF) - 1;
102 }
103
104 /* Fix high word of checksum */
105 if (LOWORD(CalcSum) >= HIWORD(HeaderSum))
106 {
107 CalcSum -= HIWORD(HeaderSum);
108 }
109 else
110 {
111 CalcSum = ((LOWORD(CalcSum) - HIWORD(HeaderSum)) & 0xFFFF) - 1;
112 }
113
114 /* Add file length */
115 CalcSum += ImageSize;
116
117 if (CalcSum != HeaderSum)
118 DPRINT1("Image %p checksum mismatches! 0x%x != 0x%x, ImageSize %x, FileLen %x\n", BaseAddress, CalcSum, HeaderSum, ImageSize, FileLength);
119
120 return (BOOLEAN)(CalcSum == HeaderSum);
121 }
122
123 /*
124 * @implemented
125 */
126 PIMAGE_NT_HEADERS
127 NTAPI
128 RtlImageNtHeader(IN PVOID BaseAddress)
129 {
130 PIMAGE_NT_HEADERS NtHeader;
131 PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)BaseAddress;
132
133 if (DosHeader && SWAPW(DosHeader->e_magic) != IMAGE_DOS_SIGNATURE)
134 {
135 DPRINT1("DosHeader->e_magic %x\n", SWAPW(DosHeader->e_magic));
136 DPRINT1("NtHeader 0x%lx\n", ((ULONG_PTR)BaseAddress + SWAPD(DosHeader->e_lfanew)));
137 }
138 else
139 {
140 NtHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)BaseAddress + SWAPD(DosHeader->e_lfanew));
141 if (SWAPD(NtHeader->Signature) == IMAGE_NT_SIGNATURE)
142 return NtHeader;
143 }
144
145 return NULL;
146 }
147
148
149 /*
150 * @implemented
151 */
152 PVOID
153 NTAPI
154 RtlImageDirectoryEntryToData(
155 PVOID BaseAddress,
156 BOOLEAN MappedAsImage,
157 USHORT Directory,
158 PULONG Size)
159 {
160 PIMAGE_NT_HEADERS NtHeader;
161 ULONG Va;
162
163 /* Magic flag for non-mapped images. */
164 if ((ULONG_PTR)BaseAddress & 1)
165 {
166 BaseAddress = (PVOID)((ULONG_PTR)BaseAddress & ~1);
167 MappedAsImage = FALSE;
168 }
169
170
171 NtHeader = RtlImageNtHeader (BaseAddress);
172 if (NtHeader == NULL)
173 return NULL;
174
175 if (Directory >= SWAPD(NtHeader->OptionalHeader.NumberOfRvaAndSizes))
176 return NULL;
177
178 Va = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].VirtualAddress);
179 if (Va == 0)
180 return NULL;
181
182 *Size = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].Size);
183
184 if (MappedAsImage || Va < SWAPD(NtHeader->OptionalHeader.SizeOfHeaders))
185 return (PVOID)((ULONG_PTR)BaseAddress + Va);
186
187 /* image mapped as ordinary file, we must find raw pointer */
188 return RtlImageRvaToVa (NtHeader, BaseAddress, Va, NULL);
189 }
190
191
192 /*
193 * @implemented
194 */
195 PIMAGE_SECTION_HEADER
196 NTAPI
197 RtlImageRvaToSection(
198 PIMAGE_NT_HEADERS NtHeader,
199 PVOID BaseAddress,
200 ULONG Rva)
201 {
202 PIMAGE_SECTION_HEADER Section;
203 ULONG Va;
204 ULONG Count;
205
206 Count = SWAPW(NtHeader->FileHeader.NumberOfSections);
207 Section = IMAGE_FIRST_SECTION(NtHeader);
208
209 while (Count--)
210 {
211 Va = SWAPD(Section->VirtualAddress);
212 if ((Va <= Rva) &&
213 (Rva < Va + SWAPD(Section->Misc.VirtualSize)))
214 return Section;
215 Section++;
216 }
217 return NULL;
218 }
219
220
221 /*
222 * @implemented
223 */
224 PVOID
225 NTAPI
226 RtlImageRvaToVa(
227 PIMAGE_NT_HEADERS NtHeader,
228 PVOID BaseAddress,
229 ULONG Rva,
230 PIMAGE_SECTION_HEADER *SectionHeader)
231 {
232 PIMAGE_SECTION_HEADER Section = NULL;
233
234 if (SectionHeader)
235 Section = *SectionHeader;
236
237 if (Section == NULL ||
238 Rva < SWAPD(Section->VirtualAddress) ||
239 Rva >= SWAPD(Section->VirtualAddress) + SWAPD(Section->Misc.VirtualSize))
240 {
241 Section = RtlImageRvaToSection (NtHeader, BaseAddress, Rva);
242 if (Section == NULL)
243 return 0;
244
245 if (SectionHeader)
246 *SectionHeader = Section;
247 }
248
249 return (PVOID)((ULONG_PTR)BaseAddress +
250 Rva +
251 SWAPD(Section->PointerToRawData) -
252 (ULONG_PTR)SWAPD(Section->VirtualAddress));
253 }
254
255 PIMAGE_BASE_RELOCATION
256 NTAPI
257 LdrProcessRelocationBlockLongLong(
258 IN ULONG_PTR Address,
259 IN ULONG Count,
260 IN PUSHORT TypeOffset,
261 IN LONGLONG Delta)
262 {
263 SHORT Offset;
264 USHORT Type;
265 USHORT i;
266 PUSHORT ShortPtr;
267 PULONG LongPtr;
268 PULONGLONG LongLongPtr;
269
270 for (i = 0; i < Count; i++)
271 {
272 Offset = SWAPW(*TypeOffset) & 0xFFF;
273 Type = SWAPW(*TypeOffset) >> 12;
274 ShortPtr = (PUSHORT)(RVA(Address, Offset));
275
276 /*
277 * Don't relocate within the relocation section itself.
278 * GCC/LD generates sometimes relocation records for the relocation section.
279 * This is a bug in GCC/LD.
280 * Fix for it disabled, since it was only in ntoskrnl and not in ntdll
281 */
282 /*
283 if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir ||
284 (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd)
285 {*/
286 switch (Type)
287 {
288 /* case IMAGE_REL_BASED_SECTION : */
289 /* case IMAGE_REL_BASED_REL32 : */
290 case IMAGE_REL_BASED_ABSOLUTE:
291 break;
292
293 case IMAGE_REL_BASED_HIGH:
294 *ShortPtr = HIWORD(MAKELONG(0, *ShortPtr) + (LONG)Delta);
295 break;
296
297 case IMAGE_REL_BASED_LOW:
298 *ShortPtr = SWAPW(*ShortPtr) + LOWORD(Delta);
299 break;
300
301 case IMAGE_REL_BASED_HIGHLOW:
302 LongPtr = (PULONG)RVA(Address, Offset);
303 *LongPtr = SWAPD(*LongPtr) + (ULONG)Delta;
304 break;
305
306 case IMAGE_REL_BASED_DIR64:
307 LongLongPtr = (PUINT64)RVA(Address, Offset);
308 *LongLongPtr = SWAPQ(*LongLongPtr) + Delta;
309 break;
310
311 case IMAGE_REL_BASED_HIGHADJ:
312 case IMAGE_REL_BASED_MIPS_JMPADDR:
313 default:
314 DPRINT1("Unknown/unsupported fixup type %hu.\n", Type);
315 DPRINT1("Address %x, Current %d, Count %d, *TypeOffset %x\n", Address, i, Count, SWAPW(*TypeOffset));
316 return (PIMAGE_BASE_RELOCATION)NULL;
317 }
318
319 TypeOffset++;
320 }
321
322 return (PIMAGE_BASE_RELOCATION)TypeOffset;
323 }
324
325 ULONG
326 NTAPI
327 LdrRelocateImageWithBias(
328 IN PVOID BaseAddress,
329 IN LONGLONG AdditionalBias,
330 IN PCCH LoaderName,
331 IN ULONG Success,
332 IN ULONG Conflict,
333 IN ULONG Invalid)
334 {
335 PIMAGE_NT_HEADERS NtHeaders;
336 PIMAGE_DATA_DIRECTORY RelocationDDir;
337 PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd;
338 ULONG Count;
339 ULONG_PTR Address;
340 PUSHORT TypeOffset;
341 LONGLONG Delta;
342
343 NtHeaders = RtlImageNtHeader(BaseAddress);
344
345 if (NtHeaders == NULL)
346 return Invalid;
347
348 if (SWAPW(NtHeaders->FileHeader.Characteristics) & IMAGE_FILE_RELOCS_STRIPPED)
349 {
350 return Conflict;
351 }
352
353 RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
354
355 if (SWAPD(RelocationDDir->VirtualAddress) == 0 || SWAPD(RelocationDDir->Size) == 0)
356 {
357 return Success;
358 }
359
360 Delta = (ULONG_PTR)BaseAddress - SWAPD(NtHeaders->OptionalHeader.ImageBase) + AdditionalBias;
361 RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseAddress + SWAPD(RelocationDDir->VirtualAddress));
362 RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + SWAPD(RelocationDDir->Size));
363
364 while (RelocationDir < RelocationEnd &&
365 SWAPW(RelocationDir->SizeOfBlock) > 0)
366 {
367 Count = (SWAPW(RelocationDir->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
368 Address = (ULONG_PTR)RVA(BaseAddress, SWAPD(RelocationDir->VirtualAddress));
369 TypeOffset = (PUSHORT)(RelocationDir + 1);
370
371 RelocationDir = LdrProcessRelocationBlockLongLong(Address,
372 Count,
373 TypeOffset,
374 Delta);
375
376 if (RelocationDir == NULL)
377 {
378 DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n");
379 return Invalid;
380 }
381 }
382
383 return Success;
384 }
385
386 /* EOF */