reshuffling of dlls
[reactos.git] / reactos / dll / imagehlp / access.c
1 /*
2 * IMAGEHLP library
3 *
4 * Copyright 1998 Patrik Stridvall
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /* INCLUDES ******************************************************************/
22
23 #include "precomp.h"
24
25 //#define NDEBUG
26 #include <debug.h>
27
28 /* DATA **********************************************************************/
29
30 BOOLEAN DllListInitialized;
31 LIST_ENTRY ImageLoadListHead;
32
33 /* FUNCTIONS *****************************************************************/
34
35 PVOID
36 IMAGEAPI
37 ImageDirectoryEntryToData32(PVOID Base,
38 BOOLEAN MappedAsImage,
39 USHORT DirectoryEntry,
40 PULONG Size,
41 PIMAGE_SECTION_HEADER *FoundHeader OPTIONAL,
42 PIMAGE_FILE_HEADER FileHeader,
43 PIMAGE_OPTIONAL_HEADER OptionalHeader)
44 {
45 ULONG i;
46 PIMAGE_SECTION_HEADER CurrentSection;
47 ULONG DirectoryEntryVA;
48
49 /* Check if this entry is invalid */
50 if (DirectoryEntry >= OptionalHeader->NumberOfRvaAndSizes)
51 {
52 /* Nothing found */
53 *Size = 0;
54 return NULL;
55 }
56
57 /* Get the VA of the Directory Requested */
58 DirectoryEntryVA = OptionalHeader->DataDirectory[DirectoryEntry].VirtualAddress;
59 if (!DirectoryEntryVA)
60 {
61 /* It doesn't exist */
62 *Size = 0;
63 return NULL;
64 }
65
66 /* Get the size of the Directory Requested */
67 *Size = OptionalHeader->DataDirectory[DirectoryEntry].Size;
68
69 /* Check if it was mapped as an image or if the entry is within the headers */
70 if ((MappedAsImage) || (DirectoryEntryVA < OptionalHeader->SizeOfHeaders))
71 {
72 /* No header found */
73 if (FoundHeader) *FoundHeader = NULL;
74
75 /* And simply return the VA */
76 return (PVOID)((ULONG_PTR)Base + DirectoryEntryVA);
77 }
78
79 /* Read the first Section */
80 CurrentSection = (PIMAGE_SECTION_HEADER)((ULONG_PTR)OptionalHeader +
81 FileHeader->SizeOfOptionalHeader);
82
83 /* Loop through every section*/
84 for (i = 0; i < FileHeader->NumberOfSections; i++)
85 {
86 /* If the Directory VA is located inside this section's VA, then this section belongs to this Directory */
87 if ((DirectoryEntryVA >= CurrentSection->VirtualAddress) &&
88 (DirectoryEntryVA < (CurrentSection->VirtualAddress +
89 CurrentSection->SizeOfRawData)))
90 {
91 /* Return the section header */
92 if (FoundHeader) *FoundHeader = CurrentSection;
93 return ((PVOID)((ULONG_PTR)Base +
94 (DirectoryEntryVA - CurrentSection->VirtualAddress) +
95 CurrentSection->PointerToRawData));
96 }
97
98 /* Move to the next section */
99 CurrentSection++;
100 }
101
102 /* If we got here, then we didn't find anything */
103 return NULL;
104 }
105
106 /*
107 * @unimplemented
108 */
109 DWORD
110 IMAGEAPI
111 GetTimestampForLoadedLibrary(HMODULE Module)
112 {
113 UNIMPLEMENTED;
114 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
115 return 0;
116 }
117
118 /*
119 * @unimplemented
120 */
121 BOOL
122 IMAGEAPI
123 GetImageConfigInformation(PLOADED_IMAGE LoadedImage,
124 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigInformation)
125 {
126 UNIMPLEMENTED;
127 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
128 return FALSE;
129 }
130
131 /*
132 * @implemented
133 */
134 DWORD
135 IMAGEAPI
136 GetImageUnusedHeaderBytes(PLOADED_IMAGE LoadedImage,
137 LPDWORD SizeUnusedHeaderBytes)
138 {
139 SIZE_T FirstFreeByte;
140 PIMAGE_OPTIONAL_HEADER OptionalHeader = NULL;
141 PIMAGE_NT_HEADERS NtHeaders;
142 ULONG i;
143
144 /* Read the NT Headers */
145 NtHeaders = LoadedImage->FileHeader;
146
147 /* Find the first free byte, which is after all the headers and sections */
148 FirstFreeByte = (ULONG_PTR)NtHeaders -
149 (ULONG_PTR)LoadedImage->MappedAddress +
150 FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
151 NtHeaders->FileHeader.SizeOfOptionalHeader +
152 NtHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
153
154 /* Get the Optional Header */
155 OptionalHeader = &LoadedImage->FileHeader->OptionalHeader;
156
157 /*
158 * There is the possibilty that one of the Data Directories is in the PE Header
159 * itself, so we'll need to find such a case and add it to our PE used space
160 */
161 for (i = 0; i < OptionalHeader->NumberOfRvaAndSizes; i++)
162 {
163 /* If the VA is less then the size of headers, then the data is inside the PE header */
164 if (OptionalHeader->DataDirectory[i].VirtualAddress <
165 OptionalHeader->SizeOfHeaders)
166 {
167 /* However, make sure it's not 0, which means it doesnt actually exist */
168 if (OptionalHeader->DataDirectory[i].VirtualAddress >=
169 FirstFreeByte)
170 {
171 /* Our first empty byte is after this Directory Data then */
172 FirstFreeByte = OptionalHeader->DataDirectory[i].VirtualAddress +
173 OptionalHeader->DataDirectory[i].Size;
174 }
175 }
176 }
177
178 /* Return the unused Header Bytes */
179 *SizeUnusedHeaderBytes = OptionalHeader->SizeOfHeaders - (DWORD)FirstFreeByte;
180
181 /* And return the first free byte*/
182 return (DWORD)FirstFreeByte;
183 }
184
185 /*
186 * @implemented
187 */
188 PVOID
189 IMAGEAPI
190 ImageDirectoryEntryToData(PVOID Base,
191 BOOLEAN MappedAsImage,
192 USHORT DirectoryEntry,
193 PULONG Size)
194 {
195 /* Let the extended function handle it */
196 return ImageDirectoryEntryToDataEx(Base,
197 MappedAsImage,
198 DirectoryEntry,
199 Size,
200 NULL);
201 }
202
203 /*
204 * @implemented
205 */
206 PVOID
207 IMAGEAPI
208 ImageDirectoryEntryToDataEx(IN PVOID Base,
209 IN BOOLEAN MappedAsImage,
210 IN USHORT DirectoryEntry,
211 OUT PULONG Size,
212 OUT PIMAGE_SECTION_HEADER *FoundSection OPTIONAL)
213 {
214 PIMAGE_NT_HEADERS NtHeader;
215 PIMAGE_FILE_HEADER FileHeader;
216 PIMAGE_OPTIONAL_HEADER OptionalHeader;
217
218 /* Get the optional header ourselves */
219 NtHeader = ImageNtHeader(Base);
220 FileHeader = &NtHeader->FileHeader;
221 OptionalHeader = &NtHeader->OptionalHeader;
222
223 /* FIXME: Read image type and call appropriate function (32, 64, ROM) */
224 return ImageDirectoryEntryToData32(Base,
225 MappedAsImage,
226 DirectoryEntry,
227 Size,
228 FoundSection,
229 FileHeader,
230 OptionalHeader);
231 }
232
233 /*
234 * @implemented
235 */
236 PLOADED_IMAGE
237 IMAGEAPI
238 ImageLoad(LPSTR DllName,
239 LPSTR DllPath)
240 {
241 PLIST_ENTRY Head, Next;
242 PLOADED_IMAGE LoadedImage;
243 CHAR Drive[_MAX_DRIVE], Dir[_MAX_DIR], Filename[_MAX_FNAME], Ext[_MAX_EXT];
244 BOOL CompleteName = TRUE;
245 CHAR FullName[MAX_PATH];
246
247 /* Initialize the List Head */
248 if (!DllListInitialized)
249 {
250 InitializeListHead(&ImageLoadListHead);
251 DllListInitialized = TRUE;
252 }
253
254 /* Move to the Next DLL */
255 Head = &ImageLoadListHead;
256 Next = Head->Flink;
257 DPRINT("Trying to find library: %s in current ListHead \n", DllName);
258
259 /* Split the path */
260 _splitpath(DllName, Drive, Dir, Filename, Ext);
261
262 /* Check if we only got a name */
263 if (!strlen(Drive) && !strlen(Dir)) CompleteName = FALSE;
264
265 /* Check if we already Loaded it */
266 while (Next != Head)
267 {
268 /* Get the Loaded Image Structure */
269 LoadedImage = CONTAINING_RECORD(Next, LOADED_IMAGE, Links);
270 DPRINT("Found: %s in current ListHead \n", LoadedImage->ModuleName);
271
272 /* Check if we didn't have a complete name */
273 if (!CompleteName)
274 {
275 /* Split this module's name */
276 _splitpath(LoadedImage->ModuleName, NULL, NULL, Filename, Ext);
277
278 /* Use only the name and extension */
279 strcpy(FullName, Filename);
280 strcat(FullName, Ext);
281 }
282 else
283 {
284 /* Use the full untouched name */
285 strcpy(FullName, LoadedImage->ModuleName);
286 }
287
288 /* Check if the Names Match */
289 if (!_stricmp(DllName, FullName))
290 {
291 DPRINT("Found it, returning it\n");
292 return LoadedImage;
293 }
294
295 /* Move to next Entry */
296 Next = Next->Flink;
297 }
298
299 /* Allocate memory for the Structure, and write the Module Name under */
300 DPRINT("Didn't find it...allocating it for you now\n");
301 LoadedImage = HeapAlloc(IMAGEHLP_hHeap,
302 0,
303 sizeof(*LoadedImage) + strlen(DllName) + 1);
304 if (LoadedImage)
305 {
306 /* Module Name will be after structure */
307 LoadedImage->ModuleName = (LPSTR)(LoadedImage + 1);
308
309 /* Copy the Module Name */
310 strcpy(LoadedImage->ModuleName, DllName);
311
312 /* Now Load it */
313 if (MapAndLoad(DllName, DllPath, LoadedImage, TRUE, TRUE))
314 {
315 /* Add it to our list and return it */
316 InsertTailList(&ImageLoadListHead, &LoadedImage->Links);
317 return LoadedImage;
318 }
319
320 /* If we're here...there's been a failure */
321 HeapFree(IMAGEHLP_hHeap, 0, LoadedImage);
322 LoadedImage = NULL;
323 }
324 return LoadedImage;
325 }
326
327 /*
328 * @implemented
329 */
330 PIMAGE_SECTION_HEADER
331 IMAGEAPI
332 ImageRvaToSection(IN PIMAGE_NT_HEADERS NtHeaders,
333 IN PVOID Base,
334 IN ULONG Rva)
335 {
336 PIMAGE_SECTION_HEADER Section;
337 ULONG i;
338
339 /* Get the First Section */
340 Section = IMAGE_FIRST_SECTION(NtHeaders);
341
342 /* Look through each section */
343 for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
344 {
345 /* Check if the RVA is in between */
346 if ((Rva >= Section->VirtualAddress) &&
347 (Rva < (Section->VirtualAddress + Section->SizeOfRawData)))
348 {
349 /* Return this section */
350 return Section;
351 }
352
353 /* Move to the next section */
354 Section++;
355 }
356
357 /* Not Found */
358 return NULL;
359 }
360
361 /*
362 * @implemented
363 */
364 PIMAGE_NT_HEADERS
365 IMAGEAPI
366 ImageNtHeader(PVOID Base)
367 {
368 /* Let RTL do it */
369 return RtlImageNtHeader(Base);
370 }
371
372 /*
373 * @implemented
374 */
375 PVOID
376 IMAGEAPI
377 ImageRvaToVa(IN PIMAGE_NT_HEADERS NtHeaders,
378 IN PVOID Base,
379 IN ULONG Rva,
380 IN OUT PIMAGE_SECTION_HEADER *LastRvaSection OPTIONAL)
381 {
382 PIMAGE_SECTION_HEADER Section;
383
384 /* Get the Section Associated */
385 Section = ImageRvaToSection(NtHeaders, Base, Rva);
386
387 /* Return it, if specified */
388 if (LastRvaSection) *LastRvaSection = Section;
389
390 /* Return the VA */
391 return (PVOID)((ULONG_PTR)Base + (Rva - Section->VirtualAddress) +
392 Section->PointerToRawData);
393 }
394
395 /*
396 * @implemented
397 */
398 BOOL
399 IMAGEAPI
400 ImageUnload(PLOADED_IMAGE LoadedImage)
401 {
402 /* If the image list isn't empty, remove this entry */
403 if (!IsListEmpty(&LoadedImage->Links)) RemoveEntryList(&LoadedImage->Links);
404
405 /* Unmap and unload it */
406 UnMapAndLoad(LoadedImage);
407
408 /* Free the structure */
409 HeapFree(IMAGEHLP_hHeap, 0, LoadedImage);
410
411 /* Return success */
412 return TRUE;
413 }
414
415 /*
416 * @implemented
417 */
418 BOOL
419 IMAGEAPI
420 MapAndLoad(LPSTR ImageName,
421 LPSTR DllPath,
422 PLOADED_IMAGE LoadedImage,
423 BOOL DotDll,
424 BOOL ReadOnly)
425 {
426 HANDLE hFile;
427 HANDLE hFileMapping;
428 ULONG Tried = 0;
429 UCHAR Buffer[MAX_PATH];
430 LPSTR FilePart;
431 LPSTR FileToOpen;
432 PIMAGE_NT_HEADERS NtHeader;
433
434 /* So we can add the DLL Path later */
435 FileToOpen = ImageName;
436
437 /* Assume failure */
438 LoadedImage->hFile = INVALID_HANDLE_VALUE;
439
440 /* Start open loop */
441 while (TRUE)
442 {
443 /* Get a handle to the file */
444 hFile = CreateFileA(FileToOpen,
445 ReadOnly ? GENERIC_READ :
446 GENERIC_READ | GENERIC_WRITE,
447 ReadOnly ? FILE_SHARE_READ :
448 FILE_SHARE_READ | FILE_SHARE_WRITE,
449 NULL,
450 OPEN_EXISTING,
451 0,
452 NULL);
453
454 if (hFile == INVALID_HANDLE_VALUE)
455 {
456 /* Check if we already tried this once */
457 if (!Tried)
458 {
459 /* We didn't do do a path search now */
460 Tried = SearchPath(DllPath,
461 ImageName,
462 DotDll ? ".dll" : ".exe",
463 MAX_PATH,
464 Buffer,
465 &FilePart);
466
467 /* Check if it was successful */
468 if (Tried && (Tried < MAX_PATH))
469 {
470 /* Change the filename to use, and try again */
471 FileToOpen = Buffer;
472 continue;
473 }
474 }
475
476 /* Fail */
477 return FALSE;
478 }
479
480 /* Success, break out */
481 break;
482 }
483
484 /* Create the File Mapping */
485 hFileMapping = CreateFileMappingA(hFile,
486 NULL,
487 ReadOnly ? PAGE_READONLY :
488 PAGE_READWRITE,
489 0,
490 0,
491 NULL);
492 if (!hFileMapping)
493 {
494 /* Fail */
495 SetLastError(GetLastError());
496 CloseHandle(hFile);
497 return FALSE;
498 }
499
500 /* Get a pointer to the file */
501 LoadedImage->MappedAddress = MapViewOfFile(hFileMapping,
502 ReadOnly ? FILE_MAP_READ :
503 FILE_MAP_WRITE,
504 0,
505 0,
506 0);
507
508 /* Close the handle to the map, we don't need it anymore */
509 CloseHandle(hFileMapping);
510
511 /* Write the image size */
512 LoadedImage->SizeOfImage = GetFileSize(hFile, NULL);
513
514 /* Get the Nt Header */
515 NtHeader = ImageNtHeader(LoadedImage->MappedAddress);
516
517 /* Allocate memory for the name and save it */
518 LoadedImage->ModuleName = HeapAlloc(IMAGEHLP_hHeap,
519 0,
520 strlen(FileToOpen) + 16);
521 strcpy(LoadedImage->ModuleName, FileToOpen);
522
523 /* Save the NT Header */
524 LoadedImage->FileHeader = NtHeader;
525
526 /* Save the section data */
527 LoadedImage->Sections = IMAGE_FIRST_SECTION(NtHeader);
528 LoadedImage->NumberOfSections = NtHeader->FileHeader.NumberOfSections;
529
530 /* Setup other data */
531 LoadedImage->SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
532 LoadedImage->Characteristics = NtHeader->FileHeader.Characteristics;
533 LoadedImage->LastRvaSection = LoadedImage->Sections;
534 LoadedImage->fSystemImage = FALSE; /* FIXME */
535 LoadedImage->fDOSImage = FALSE; /* FIXME */
536 InitializeListHead(&LoadedImage->Links);
537
538 /* Check if it was read-only */
539 if (ReadOnly)
540 {
541 /* It was, so close our handle and write it as invalid */
542 CloseHandle(hFile);
543 LoadedImage->hFile = INVALID_HANDLE_VALUE;
544 }
545 else
546 {
547 /* Write our file handle */
548 LoadedImage->hFile = hFile;
549 }
550
551 /* Return Success */
552 return TRUE;
553 }
554
555 /*
556 * @unimplemented
557 */
558 BOOL
559 IMAGEAPI
560 SetImageConfigInformation(PLOADED_IMAGE LoadedImage,
561 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigInformation)
562 {
563 UNIMPLEMENTED;
564 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
565 return FALSE;
566 }
567
568 /*
569 * @implemented
570 */
571 BOOL
572 IMAGEAPI
573 UnMapAndLoad(PLOADED_IMAGE Image)
574 {
575 PIMAGE_NT_HEADERS NtHeader;
576 DWORD HeaderCheckSum, CheckSum;
577
578 /* Check if the image was read-only */
579 if (Image->hFile == INVALID_HANDLE_VALUE)
580 {
581 /* We'll only unmap the view */
582 UnmapViewOfFile(Image->MappedAddress);
583 }
584 else
585 {
586 /* Calculate the checksum */
587 CheckSumMappedFile(Image->MappedAddress,
588 Image->SizeOfImage,
589 &HeaderCheckSum,
590 &CheckSum);
591
592 /* Get the NT Header */
593 NtHeader = Image->FileHeader;
594
595 /* Write the new checksum to it */
596 NtHeader->OptionalHeader.CheckSum = CheckSum;
597
598 /* Now flush and unmap the image */
599 FlushViewOfFile(Image->MappedAddress, Image->SizeOfImage);
600 UnmapViewOfFile(Image->MappedAddress);
601
602 /* Check if the size changed */
603 if (Image->SizeOfImage != GetFileSize(Image->hFile, NULL))
604 {
605 /* Update the file pointer */
606 SetFilePointer(Image->hFile, Image->SizeOfImage, NULL, FILE_BEGIN);
607 SetEndOfFile(Image->hFile);
608 }
609 }
610
611 /* Check if the image had a valid handle, and close it */
612 if (Image->hFile != INVALID_HANDLE_VALUE) CloseHandle(Image->hFile);
613
614 /* Return success */
615 return TRUE;
616 }
617
618 BOOL
619 IMAGEAPI
620 UnloadAllImages(VOID)
621 {
622 PLIST_ENTRY Head, Entry;
623 PLOADED_IMAGE CurrentImage;
624
625 /* Make sure we're initialized */
626 if (!DllListInitialized) return TRUE;
627
628 /* Get the list pointers and loop */
629 Head = &ImageLoadListHead;
630 Entry = Head->Flink;
631 while (Entry != Head)
632 {
633 /* Get this image */
634 CurrentImage = CONTAINING_RECORD(Entry, LOADED_IMAGE, Links);
635
636 /* Move to the next entry */
637 Entry = Entry->Flink;
638
639 /* Unload it */
640 ImageUnload(CurrentImage);
641 }
642
643 /* We are not initialized anymore */
644 DllListInitialized = FALSE;
645 return TRUE;
646 }