branch update for HEAD-2003050101
[reactos.git] / lib / ntdll / ldr / utils.c
1 /* $Id$
2  * 
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            lib/ntdll/ldr/utils.c
6  * PURPOSE:         Process startup for PE executables
7  * PROGRAMMERS:     Jean Michault
8  *                  Rex Jolliff (rex@lvcablemodem.com)
9  */
10
11 /*
12  * TODO:
13  *      - Fix calling of entry points
14  *      - Handle loading flags correctly
15  *      - any more ??
16  */
17
18 /* INCLUDES *****************************************************************/
19
20 #include <reactos/config.h>
21 #include <ddk/ntddk.h>
22 #include <windows.h>
23 #include <string.h>
24 #include <wchar.h>
25 #include <ntdll/ldr.h>
26 #include <ntos/minmax.h>
27
28
29 #ifdef DBG_NTDLL_LDR_UTILS
30 #define NDEBUG
31 #endif
32 #include <ntdll/ntdll.h>
33
34 /* PROTOTYPES ****************************************************************/
35
36 static NTSTATUS LdrFindEntryForName(PUNICODE_STRING Name, PLDR_MODULE *Module);
37 static PVOID LdrFixupForward(PCHAR ForwardName);
38 static PVOID LdrGetExportByName(PVOID BaseAddress, PUCHAR SymbolName, USHORT Hint);
39
40
41 /* FUNCTIONS *****************************************************************/
42
43
44 #ifdef DBG
45
46 VOID
47 LdrpLoadUserModuleSymbols(PLDR_MODULE LdrModule)
48 {
49   NtSystemDebugControl(
50     DebugDbgLoadSymbols,
51     (PVOID)LdrModule,
52     0,
53     NULL,
54     0,
55     NULL);
56 }
57
58 #endif /* DBG */
59
60
61 /***************************************************************************
62  * NAME                                                         LOCAL
63  *      LdrAdjustDllName
64  *
65  * DESCRIPTION
66  *      Adjusts the name of a dll to a fully qualified name.
67  *
68  * ARGUMENTS
69  *      FullDllName:    Pointer to caller supplied storage for the fully
70  *                      qualified dll name.
71  *      DllName:        Pointer to the dll name.
72  *      BaseName:       TRUE:  Only the file name is passed to FullDllName
73  *                      FALSE: The full path is preserved in FullDllName
74  *
75  * RETURN VALUE
76  *      None
77  *
78  * REVISIONS
79  *
80  * NOTE
81  *      A given path is not affected by the adjustment, but the file
82  *      name only:
83  *        ntdll      --> ntdll.dll
84  *        ntdll.     --> ntdll
85  *        ntdll.xyz  --> ntdll.xyz
86  */
87
88 static VOID
89 LdrAdjustDllName (PUNICODE_STRING FullDllName,
90                   PUNICODE_STRING DllName,
91                   BOOLEAN BaseName)
92 {
93    WCHAR Buffer[MAX_PATH];
94    ULONG Length;
95    PWCHAR Extension;
96    PWCHAR Pointer;
97
98    Length = DllName->Length / sizeof(WCHAR);
99
100    if (BaseName == TRUE)
101      {
102         /* get the base dll name */
103         Pointer = DllName->Buffer + Length;
104         Extension = Pointer;
105
106         do
107           {
108              --Pointer;
109           }
110         while (Pointer >= DllName->Buffer && *Pointer != L'\\' && *Pointer != L'/');
111
112         Pointer++;
113         Length = Extension - Pointer;
114         memmove (Buffer, Pointer, Length * sizeof(WCHAR));
115         Buffer[Length] = L'\0';
116      }
117    else
118      {
119         /* get the full dll name */
120         memmove (Buffer, DllName->Buffer, DllName->Length);
121         Buffer[DllName->Length / sizeof(WCHAR)] = L'\0';
122      }
123
124    /* Build the DLL's absolute name */
125    Extension = wcsrchr (Buffer, L'.');
126    if ((Extension != NULL) && (*Extension == L'.'))
127      {
128         /* with extension - remove dot if it's the last character */
129         if (Buffer[Length - 1] == L'.')
130                         Length--;
131         Buffer[Length] = 0;
132      }
133    else
134      {
135         /* name without extension - assume that it is .dll */
136         memmove (Buffer + Length, L".dll", 10);
137      }
138
139    RtlCreateUnicodeString(FullDllName, Buffer);
140 }
141
142 PLDR_MODULE
143 LdrAddModuleEntry(PVOID ImageBase, PIMAGE_NT_HEADERS NTHeaders,
144                   PWSTR FullDosName)
145 {
146   PLDR_MODULE           Module;
147   Module = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof (LDR_MODULE));
148   assert(Module);
149   Module->BaseAddress = (PVOID)ImageBase;
150   Module->EntryPoint = NTHeaders->OptionalHeader.AddressOfEntryPoint;
151   if (Module->EntryPoint != 0)
152     Module->EntryPoint += (ULONG)Module->BaseAddress;
153   Module->SizeOfImage = NTHeaders->OptionalHeader.SizeOfImage;
154   if (NtCurrentPeb()->Ldr->Initialized == TRUE)
155     {
156       /* loading while app is running */
157       Module->LoadCount = 1;
158     } else {
159       /*
160        * loading while app is initializing
161        * dll must not be unloaded
162        */
163       Module->LoadCount = -1;
164     }
165
166   Module->TlsIndex = 0;
167   Module->CheckSum = NTHeaders->OptionalHeader.CheckSum;
168   Module->TimeDateStamp = NTHeaders->FileHeader.TimeDateStamp;
169
170   RtlCreateUnicodeString (&Module->FullDllName,
171                           FullDosName);
172   RtlCreateUnicodeString (&Module->BaseDllName,
173                           wcsrchr(FullDosName, L'\\') + 1);
174   DPRINT ("BaseDllName %wZ\n", &Module->BaseDllName);
175   
176   /* FIXME: aquire loader lock */
177   InsertTailList(&NtCurrentPeb()->Ldr->InLoadOrderModuleList,
178                  &Module->InLoadOrderModuleList);
179   InsertTailList(&NtCurrentPeb()->Ldr->InInitializationOrderModuleList,
180                  &Module->InInitializationOrderModuleList);
181   /* FIXME: release loader lock */
182
183   return(Module);
184 }
185
186 /***************************************************************************
187  * NAME                                                         EXPORTED
188  *      LdrLoadDll
189  *
190  * DESCRIPTION
191  *
192  * ARGUMENTS
193  *
194  * RETURN VALUE
195  *
196  * REVISIONS
197  *
198  * NOTE
199  *
200  */
201
202 NTSTATUS STDCALL
203 LdrLoadDll (IN PWSTR SearchPath OPTIONAL,
204             IN ULONG LoadFlags,
205             IN PUNICODE_STRING Name,
206             OUT PVOID *BaseAddress OPTIONAL)
207 {
208   WCHAR                 SearchPathBuffer[MAX_PATH];
209   WCHAR                 FullDosName[MAX_PATH];
210   UNICODE_STRING        AdjustedName;
211   UNICODE_STRING        FullNtFileName;
212   OBJECT_ATTRIBUTES     FileObjectAttributes;
213   char                  BlockBuffer [1024];
214   PIMAGE_DOS_HEADER     DosHeader;
215   NTSTATUS              Status;
216   PIMAGE_NT_HEADERS     NTHeaders;
217   ULONG                 ImageSize;
218   ULONG                 InitialViewSize;
219   PVOID                 ImageBase;
220   HANDLE                FileHandle;
221   HANDLE                SectionHandle;
222   PDLLMAIN_FUNC         Entrypoint = NULL;
223   PLDR_MODULE           Module;
224   
225   if (Name == NULL)
226     {
227       *BaseAddress = NtCurrentPeb()->ImageBaseAddress;
228       return STATUS_SUCCESS;
229     }
230
231
232   *BaseAddress = NULL;
233   
234   DPRINT("LdrLoadDll(Name \"%wZ\" BaseAddress %x)\n",
235          Name, BaseAddress);
236   
237   /* adjust the full dll name */
238   LdrAdjustDllName (&AdjustedName,
239                     Name,
240                     FALSE);
241   DPRINT("AdjustedName: %wZ\n", &AdjustedName);
242   
243   /*
244    * Test if dll is already loaded.
245    */
246   if (LdrFindEntryForName(&AdjustedName, &Module) == STATUS_SUCCESS)
247     {
248       DPRINT("DLL %wZ already loaded.\n", &AdjustedName);
249       if (Module->LoadCount != -1)
250         Module->LoadCount++;
251       *BaseAddress = Module->BaseAddress;
252       return STATUS_SUCCESS;
253     }
254   DPRINT("Loading \"%wZ\"\n", Name);
255
256   if (SearchPath == NULL)
257     {
258       SearchPath = SearchPathBuffer;
259       wcscpy (SearchPathBuffer, SharedUserData->NtSystemRoot);
260       wcscat (SearchPathBuffer, L"\\system32;");
261       wcscat (SearchPathBuffer, SharedUserData->NtSystemRoot);
262       wcscat (SearchPathBuffer, L";.");
263     }
264
265   DPRINT("SearchPath %S\n", SearchPath);
266   
267   if (RtlDosSearchPath_U (SearchPath,
268                           AdjustedName.Buffer,
269                           NULL,
270                           MAX_PATH,
271                           FullDosName,
272                           NULL) == 0)
273     return STATUS_DLL_NOT_FOUND;
274   
275   DPRINT("FullDosName %S\n", FullDosName);
276   
277   RtlFreeUnicodeString (&AdjustedName);
278   
279   if (!RtlDosPathNameToNtPathName_U (FullDosName,
280                                      &FullNtFileName,
281                                      NULL,
282                                      NULL))
283     return STATUS_DLL_NOT_FOUND;
284   
285   DPRINT("FullNtFileName %wZ\n", &FullNtFileName);
286   
287   InitializeObjectAttributes(&FileObjectAttributes,
288                              &FullNtFileName,
289                              0,
290                              NULL,
291                              NULL);
292   
293   DPRINT("Opening dll \"%wZ\"\n", &FullNtFileName);
294   
295   Status = ZwOpenFile(&FileHandle,
296                       FILE_ALL_ACCESS,
297                       &FileObjectAttributes, 
298                       NULL,
299                       0,
300                       0);
301   if (!NT_SUCCESS(Status))
302     {
303       DbgPrint("Dll open of %wZ failed: Status = 0x%08x\n", 
304                &FullNtFileName, Status);
305       RtlFreeUnicodeString (&FullNtFileName);
306       return Status;
307     }
308   RtlFreeUnicodeString (&FullNtFileName);
309   
310   Status = ZwReadFile(FileHandle,
311                       0,
312                       0,
313                       0,
314                       0,
315                       BlockBuffer,
316                       sizeof(BlockBuffer),
317                       0,
318                       0);
319   if (!NT_SUCCESS(Status))
320     {
321       DPRINT("Dll header read failed: Status = 0x%08x\n", Status);
322       ZwClose(FileHandle);
323       return Status;
324     }
325   /*
326    * Overlay DOS and NT headers structures to the 
327    * buffer with DLL's header raw data.
328    */
329   DosHeader = (PIMAGE_DOS_HEADER) BlockBuffer;
330   NTHeaders = (PIMAGE_NT_HEADERS) (BlockBuffer + DosHeader->e_lfanew);
331   /*
332    * Check it is a PE image file.
333    */
334   if ((DosHeader->e_magic != IMAGE_DOS_MAGIC)
335       || (DosHeader->e_lfanew == 0L)
336       || (*(PULONG)(NTHeaders) != IMAGE_PE_MAGIC))
337     {
338       DPRINT("NTDLL format invalid\n");
339       ZwClose(FileHandle);
340       
341       return STATUS_UNSUCCESSFUL;
342     }
343   
344   ImageBase = (PVOID) NTHeaders->OptionalHeader.ImageBase;
345   ImageSize = NTHeaders->OptionalHeader.SizeOfImage;
346   
347   DPRINT("ImageBase 0x%08x\n", ImageBase);
348         
349   /*
350    * Create a section for dll.
351    */
352   Status = ZwCreateSection(&SectionHandle,
353                            SECTION_ALL_ACCESS,
354                            NULL,
355                            NULL,
356                            PAGE_READWRITE,
357                            SEC_COMMIT | SEC_IMAGE,
358                            FileHandle);
359   if (!NT_SUCCESS(Status))
360     {
361       DPRINT("NTDLL create section failed: Status = 0x%08x\n", Status);
362       ZwClose(FileHandle);
363       return Status;
364     }
365   
366   /*
367    * Map the dll into the process.
368    */
369   InitialViewSize = 0;
370   ImageBase = 0;
371   Status = ZwMapViewOfSection(SectionHandle,
372                               NtCurrentProcess(),
373                               &ImageBase,
374                               0,
375                               InitialViewSize,
376                               NULL,
377                               &InitialViewSize,
378                               0,
379                               MEM_COMMIT,
380                               PAGE_READWRITE);
381   if (!NT_SUCCESS(Status))
382     {
383       DbgPrint("NTDLL.LDR: map view of section failed (Status %x)\n", Status);
384       ZwClose(FileHandle);
385       return(Status);
386     }
387   ZwClose(FileHandle);
388
389   /* relocate dll and fixup import table */
390   if ((NTHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) ==
391       IMAGE_FILE_DLL)
392     {
393       Entrypoint =
394         (PDLLMAIN_FUNC) LdrPEStartup(ImageBase, SectionHandle, &Module,
395                                      FullDosName);
396       if (Entrypoint == NULL)
397         {
398           return(STATUS_UNSUCCESSFUL);
399         }
400     }
401   
402 #ifdef DBG
403
404   LdrpLoadUserModuleSymbols(Module);
405
406 #endif /* DBG */
407
408   /* initialize dll */
409   if ((NTHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) ==
410       IMAGE_FILE_DLL)
411     {
412       if (Module->EntryPoint != 0)
413         {
414           Entrypoint = (PDLLMAIN_FUNC)Module->EntryPoint;
415           
416           DPRINT("Calling entry point at 0x%08x\n", Entrypoint);
417           if (FALSE == Entrypoint(Module->BaseAddress,
418                                   DLL_PROCESS_ATTACH,
419                                   NULL))
420             {
421               /* Do this as a DPRINT1 for now, until clean up and fail implemented */
422               DPRINT1("NTDLL.LDR: DLL \"%wZ\" failed to initialize\n",
423                      &Module->BaseDllName);
424               /* FIXME: should clean up and fail */
425             }
426           else
427             {
428               DPRINT("NTDLL.LDR: DLL \"%wZ\" initialized successfully\n",
429                      &Module->BaseDllName);
430             }
431         }
432       else
433         {
434           DPRINT("NTDLL.LDR: Entrypoint is NULL for \"%wZ\"\n",
435                  &Module->BaseDllName);
436         }
437     }
438
439   *BaseAddress = Module->BaseAddress;
440   return STATUS_SUCCESS;
441 }
442
443
444 /***************************************************************************
445  * NAME                                                         EXPORTED
446  *      LdrFindEntryForAddress
447  *
448  * DESCRIPTION
449  *
450  * ARGUMENTS
451  *
452  * RETURN VALUE
453  *
454  * REVISIONS
455  *
456  * NOTE
457  *
458  */
459 NTSTATUS STDCALL
460 LdrFindEntryForAddress(PVOID Address,
461                        PLDR_MODULE *Module)
462 {
463   PLIST_ENTRY ModuleListHead;
464   PLIST_ENTRY Entry;
465   PLDR_MODULE ModulePtr;
466
467   DPRINT("NTDLL.LdrFindEntryForAddress(Address %p)\n", Address);
468
469   if (NtCurrentPeb()->Ldr == NULL)
470     return(STATUS_NO_MORE_ENTRIES);
471
472   ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
473   Entry = ModuleListHead->Flink;
474   if (Entry == ModuleListHead)
475     return(STATUS_NO_MORE_ENTRIES);
476
477   while (Entry != ModuleListHead)
478     {
479       ModulePtr = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
480
481       DPRINT("Scanning %wZ at %p\n", &ModulePtr->BaseDllName, ModulePtr->BaseAddress);
482
483       if ((Address >= ModulePtr->BaseAddress) &&
484           (Address <= (ModulePtr->BaseAddress + ModulePtr->SizeOfImage)))
485         {
486           *Module = ModulePtr;
487           return(STATUS_SUCCESS);
488         }
489
490       Entry = Entry->Flink;
491     }
492
493   DPRINT("Failed to find module entry.\n");
494
495   return(STATUS_NO_MORE_ENTRIES);
496 }
497
498
499 /***************************************************************************
500  * NAME                                                         LOCAL
501  *      LdrFindEntryForName
502  *
503  * DESCRIPTION
504  *
505  * ARGUMENTS
506  *
507  * RETURN VALUE
508  *
509  * REVISIONS
510  *
511  * NOTE
512  *
513  */
514 static NTSTATUS
515 LdrFindEntryForName(PUNICODE_STRING Name,
516                     PLDR_MODULE *Module)
517 {
518   PLIST_ENTRY ModuleListHead;
519   PLIST_ENTRY Entry;
520   PLDR_MODULE ModulePtr;
521   BOOLEAN ContainsPath;
522   unsigned i;
523
524   DPRINT("NTDLL.LdrFindEntryForName(Name %wZ)\n", Name);
525
526   if (NtCurrentPeb()->Ldr == NULL)
527     return(STATUS_NO_MORE_ENTRIES);
528
529   ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
530   Entry = ModuleListHead->Flink;
531   if (Entry == ModuleListHead)
532     return(STATUS_NO_MORE_ENTRIES);
533
534   // NULL is the current process
535   if (Name == NULL)
536     {
537       *Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
538       return(STATUS_SUCCESS);
539     }
540
541   ContainsPath = (2 <= Name->Length && L':' == Name->Buffer[1]);
542   for (i = 0; ! ContainsPath && i < Name->Length; i++)
543     {
544     ContainsPath = L'\\' == Name->Buffer[i] ||
545                    L'/' == Name->Buffer[i];
546     }
547   while (Entry != ModuleListHead)
548     {
549       ModulePtr = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
550
551       DPRINT("Scanning %wZ %wZ\n", &ModulePtr->BaseDllName, Name);
552
553       if ((! ContainsPath &&
554            0 == RtlCompareUnicodeString(&ModulePtr->BaseDllName, Name, TRUE)) ||
555           (ContainsPath &&
556            0 == RtlCompareUnicodeString(&ModulePtr->FullDllName, Name, TRUE)))
557         {
558           *Module = ModulePtr;
559           return(STATUS_SUCCESS);
560         }
561
562       Entry = Entry->Flink;
563     }
564
565   DPRINT("Failed to find dll %wZ\n", Name);
566
567   return(STATUS_NO_MORE_ENTRIES);
568 }
569
570 /**********************************************************************
571  * NAME                                                         LOCAL
572  *      LdrFixupForward
573  *
574  * DESCRIPTION
575  *
576  * ARGUMENTS
577  *
578  * RETURN VALUE
579  *
580  * REVISIONS
581  *
582  * NOTE
583  *
584  */
585 static PVOID
586 LdrFixupForward(PCHAR ForwardName)
587 {
588    CHAR NameBuffer[128];
589    UNICODE_STRING DllName;
590    UNICODE_STRING FunctionName;
591    NTSTATUS Status;
592    PCHAR p;
593    PVOID BaseAddress;
594
595    strcpy(NameBuffer, ForwardName);
596    p = strchr(NameBuffer, '.');
597    if (p != NULL)
598      {
599         *p = 0;
600
601         DPRINT("Dll: %s  Function: %s\n", NameBuffer, p+1);
602         RtlCreateUnicodeStringFromAsciiz (&DllName,
603                                           NameBuffer);
604
605         Status = LdrGetDllHandle (0, 0, &DllName, &BaseAddress);
606         if (!NT_SUCCESS(Status))
607           {
608              Status = LdrLoadDll(NULL,
609                                  0,
610                                  &DllName,
611                                  &BaseAddress);
612              if (!NT_SUCCESS(Status))
613                {
614                   DbgPrint("LdrFixupForward: failed to load %wZ\n", &DllName);
615                   RtlFreeUnicodeString (&DllName);
616                   return NULL;
617                }
618           }
619
620         RtlFreeUnicodeString (&DllName);
621         DPRINT("BaseAddress: %p\n", BaseAddress);
622         
623         return LdrGetExportByName(BaseAddress, p+1, -1);
624      }
625
626    return NULL;
627 }
628
629
630 /**********************************************************************
631  * NAME                                                         LOCAL
632  *      LdrGetExportByOrdinal
633  *      
634  * DESCRIPTION
635  *
636  * ARGUMENTS
637  *
638  * RETURN VALUE
639  *
640  * REVISIONS
641  *
642  * NOTE
643  *
644  */
645 static PVOID
646 LdrGetExportByOrdinal (
647         PVOID   BaseAddress,
648         ULONG   Ordinal
649         )
650 {
651         PIMAGE_EXPORT_DIRECTORY ExportDir;
652         PDWORD                  * ExFunctions;
653         USHORT                  * ExOrdinals;
654
655         ExportDir = (PIMAGE_EXPORT_DIRECTORY)
656                 RtlImageDirectoryEntryToData (BaseAddress,
657                                               TRUE,
658                                               IMAGE_DIRECTORY_ENTRY_EXPORT,
659                                               NULL);
660
661
662         ExOrdinals = (USHORT *)
663                 RVA(
664                         BaseAddress,
665                         ExportDir->AddressOfNameOrdinals
666                         );
667         ExFunctions = (PDWORD *)
668                 RVA(
669                         BaseAddress,
670                         ExportDir->AddressOfFunctions
671                         );
672         DbgPrint(
673                 "LdrGetExportByOrdinal(Ordinal %d) = %x\n",
674                 Ordinal,
675                 RVA(BaseAddress, ExFunctions[Ordinal - ExportDir->Base] )
676                 );
677         return(RVA(BaseAddress, ExFunctions[Ordinal - ExportDir->Base] ));
678 }
679
680
681 /**********************************************************************
682  * NAME                                                         LOCAL
683  *      LdrGetExportByName
684  *      
685  * DESCRIPTION
686  *
687  * ARGUMENTS
688  *
689  * RETURN VALUE
690  *
691  * REVISIONS
692  *
693  * NOTE
694  *  AddressOfNames and AddressOfNameOrdinals are paralell tables, 
695  *  both with NumberOfNames entries.
696  *
697  */
698 static PVOID
699 LdrGetExportByName(PVOID BaseAddress,
700                    PUCHAR SymbolName,
701                    WORD Hint)
702 {
703    PIMAGE_EXPORT_DIRECTORY      ExportDir;
704    PDWORD                       * ExFunctions;
705    PDWORD                       * ExNames;
706    USHORT                       * ExOrdinals;
707    ULONG                        i;
708    PVOID                        ExName;
709    ULONG                        Ordinal;
710    PVOID                        Function;
711    ULONG minn, maxn;
712    ULONG ExportDirSize;
713    
714    DPRINT("LdrGetExportByName %x %s %hu\n", BaseAddress, SymbolName, Hint);
715
716    ExportDir = (PIMAGE_EXPORT_DIRECTORY)
717      RtlImageDirectoryEntryToData(BaseAddress,
718                                   TRUE,
719                                   IMAGE_DIRECTORY_ENTRY_EXPORT,
720                                   &ExportDirSize);
721    if (ExportDir == NULL)
722      {
723         DbgPrint("LdrGetExportByName(): no export directory!\n");
724         return NULL;
725      }
726
727
728    //The symbol names may be missing entirely
729    if (ExportDir->AddressOfNames == 0)
730    {
731       DPRINT("LdrGetExportByName(): symbol names missing entirely\n");  
732       return NULL;
733    }
734
735    /*
736     * Get header pointers
737     */
738    ExNames = (PDWORD *)RVA(BaseAddress,
739                            ExportDir->AddressOfNames);
740    ExOrdinals = (USHORT *)RVA(BaseAddress,
741                               ExportDir->AddressOfNameOrdinals);
742    ExFunctions = (PDWORD *)RVA(BaseAddress,
743                                ExportDir->AddressOfFunctions);
744    
745    /*
746     * Check the hint first
747     */
748    if (Hint < ExportDir->NumberOfNames)
749      {
750         ExName = RVA(BaseAddress, ExNames[Hint]);
751         if (strcmp(ExName, SymbolName) == 0)
752           {
753              Ordinal = ExOrdinals[Hint];
754              Function = RVA(BaseAddress, ExFunctions[Ordinal]);
755              if (((ULONG)Function >= (ULONG)ExportDir) &&
756                  ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize))
757                {
758                   DPRINT("Forward: %s\n", (PCHAR)Function);
759                   Function = LdrFixupForward((PCHAR)Function);
760                }
761              if (Function != NULL)
762                return Function;
763           }
764      }
765    
766    /*
767     * Try a binary search first
768     */
769    minn = 0;
770    maxn = ExportDir->NumberOfNames;
771    while (minn <= maxn)
772      {
773         ULONG mid;
774         LONG res;
775
776         mid = (minn + maxn) / 2;
777
778         ExName = RVA(BaseAddress, ExNames[mid]);
779         res = strcmp(ExName, SymbolName);
780         if (res == 0)
781           {
782              Ordinal = ExOrdinals[mid];
783              Function = RVA(BaseAddress, ExFunctions[Ordinal]);
784              if (((ULONG)Function >= (ULONG)ExportDir) &&
785                  ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize))
786                {
787                   DPRINT("Forward: %s\n", (PCHAR)Function);
788                   Function = LdrFixupForward((PCHAR)Function);
789                }
790              if (Function != NULL)
791                return Function;
792           }
793         else if (minn == maxn)
794           {
795              DPRINT("LdrGetExportByName(): binary search failed\n");
796              break;
797           }
798         else if (res > 0)
799           {
800              maxn = mid - 1;
801           }
802         else
803           {
804              minn = mid + 1;
805           }
806      }
807    
808    /*
809     * Fall back on a linear search
810     */
811    DPRINT("LdrGetExportByName(): Falling back on a linear search of export table\n");
812    for (i = 0; i < ExportDir->NumberOfNames; i++)
813      {
814         ExName = RVA(BaseAddress, ExNames[i]);
815         if (strcmp(ExName,SymbolName) == 0)
816           {
817              Ordinal = ExOrdinals[i];
818              Function = RVA(BaseAddress, ExFunctions[Ordinal]);
819              DPRINT("%x %x %x\n", Function, ExportDir, ExportDir + ExportDirSize);
820              if (((ULONG)Function >= (ULONG)ExportDir) &&
821                  ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize))
822                {
823                   DPRINT("Forward: %s\n", (PCHAR)Function);
824                   Function = LdrFixupForward((PCHAR)Function);
825                }
826              return Function;
827           }
828      }
829    DbgPrint("LdrGetExportByName(): failed to find %s\n",SymbolName);
830    return NULL;
831 }
832
833
834 /**********************************************************************
835  * NAME                                                         LOCAL
836  *      LdrPerformRelocations
837  *      
838  * DESCRIPTION
839  *      Relocate a DLL's memory image.
840  *      
841  * ARGUMENTS
842  *
843  * RETURN VALUE
844  *
845  * REVISIONS
846  *
847  * NOTE
848  *
849  */
850 static NTSTATUS LdrPerformRelocations (PIMAGE_NT_HEADERS        NTHeaders,
851                                        PVOID                    ImageBase)
852 {
853   USHORT                        NumberOfEntries;
854   PUSHORT                       pValue16;
855   ULONG                 RelocationRVA;
856   ULONG                 Delta32;
857   ULONG                 Offset;
858   PULONG                        pValue32;
859   PRELOCATION_DIRECTORY RelocationDir;
860   PRELOCATION_ENTRY     RelocationBlock;
861   int                   i;
862   PIMAGE_DATA_DIRECTORY RelocationDDir;
863   ULONG OldProtect;
864   ULONG OldProtect2;
865   NTSTATUS Status;
866   PIMAGE_SECTION_HEADER Sections;
867   ULONG MaxExtend;
868   ULONG LastOffset;
869
870   Sections = 
871     (PIMAGE_SECTION_HEADER)((PVOID)NTHeaders + sizeof(IMAGE_NT_HEADERS));
872   MaxExtend = 0;
873   for (i = 0; i < NTHeaders->FileHeader.NumberOfSections; i++)
874     {
875       if (!(Sections[i].Characteristics & IMAGE_SECTION_NOLOAD))
876         {
877           ULONG Extend;
878           Extend = 
879             (ULONG)(Sections[i].VirtualAddress + Sections[i].Misc.VirtualSize);
880           MaxExtend = max(MaxExtend, Extend);
881         }
882     }
883   
884   RelocationDDir = 
885     &NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
886   RelocationRVA = RelocationDDir->VirtualAddress;
887
888   if (RelocationRVA)
889     {
890       RelocationDir = 
891         (PRELOCATION_DIRECTORY)((PCHAR)ImageBase + RelocationRVA);
892
893       while (RelocationDir->SizeOfBlock)
894         {
895           if (RelocationDir->VirtualAddress > MaxExtend)
896             {
897               RelocationRVA += RelocationDir->SizeOfBlock;
898               RelocationDir = 
899                 (PRELOCATION_DIRECTORY) (ImageBase + RelocationRVA);
900               continue;
901             }
902
903           Delta32 = (ULONG)(ImageBase - NTHeaders->OptionalHeader.ImageBase);
904           RelocationBlock = 
905             (PRELOCATION_ENTRY) (RelocationRVA + ImageBase + 
906                                  sizeof (RELOCATION_DIRECTORY));          
907           NumberOfEntries = 
908             RelocationDir->SizeOfBlock - sizeof (RELOCATION_DIRECTORY);
909           NumberOfEntries = NumberOfEntries / sizeof (RELOCATION_ENTRY);
910
911           Status = NtProtectVirtualMemory(NtCurrentProcess(),
912                                           ImageBase + 
913                                           RelocationDir->VirtualAddress,
914                                           PAGE_SIZE,
915                                           PAGE_READWRITE,
916                                           &OldProtect);
917           if (!NT_SUCCESS(Status))
918             {
919               DPRINT1("Failed to unprotect relocation target.\n");
920               return(Status);
921             }
922
923           if (RelocationDir->VirtualAddress + PAGE_SIZE < MaxExtend)
924             {
925                   Status = NtProtectVirtualMemory(NtCurrentProcess(),
926                                                   ImageBase + 
927                                                   RelocationDir->VirtualAddress + PAGE_SIZE,
928                                                   PAGE_SIZE,
929                                                   PAGE_READWRITE,
930                                                   &OldProtect2);
931                   if (!NT_SUCCESS(Status))
932                     {
933                       DPRINT1("Failed to unprotect relocation target (2).\n");
934                   NtProtectVirtualMemory(NtCurrentProcess(),
935                                         ImageBase + 
936                                         RelocationDir->VirtualAddress,
937                                         PAGE_SIZE,
938                                         OldProtect,
939                                         &OldProtect);
940                       return(Status);
941                     }
942               }
943                 
944           for (i = 0; i < NumberOfEntries; i++)
945             {
946               Offset = (RelocationBlock[i].TypeOffset & 0xfff);
947               Offset += (ULONG)(RelocationDir->VirtualAddress + ImageBase);
948
949               /*
950                * What kind of relocations should we perform
951                * for the current entry?
952                */
953               switch (RelocationBlock[i].TypeOffset >> 12)
954                 {
955                 case TYPE_RELOC_ABSOLUTE:
956                   break;
957                   
958                 case TYPE_RELOC_HIGH:
959                   pValue16 = (PUSHORT)Offset;
960                   *pValue16 += Delta32 >> 16;
961                   break;
962                   
963                 case TYPE_RELOC_LOW:
964                   pValue16 = (PUSHORT)Offset;
965                   *pValue16 += Delta32 & 0xffff;
966                   break;
967                   
968                 case TYPE_RELOC_HIGHLOW:
969                   pValue32 = (PULONG)Offset;
970                   *pValue32 += Delta32;
971                   break;
972                           
973                 case TYPE_RELOC_HIGHADJ:
974                   /* FIXME: do the highadjust fixup  */
975                   DPRINT("TYPE_RELOC_HIGHADJ fixup not implemented, sorry\n");
976                   return(STATUS_UNSUCCESSFUL);
977                   
978                 default:
979                   DPRINT("unexpected fixup type\n");
980                   return STATUS_UNSUCCESSFUL;
981                 }             
982             }
983
984           Status = NtProtectVirtualMemory(NtCurrentProcess(),
985                                           ImageBase + 
986                                           RelocationDir->VirtualAddress,
987                                           PAGE_SIZE,
988                                           OldProtect,
989                                           &OldProtect);
990           if (!NT_SUCCESS(Status))
991             {
992               DPRINT1("Failed to protect relocation target.\n");
993               return(Status);
994             }
995
996           if (RelocationDir->VirtualAddress + PAGE_SIZE < MaxExtend)
997             {
998                   Status = NtProtectVirtualMemory(NtCurrentProcess(),
999                                                   ImageBase + 
1000                                                   RelocationDir->VirtualAddress + PAGE_SIZE,
1001                                                   PAGE_SIZE,
1002                                                   OldProtect2,
1003                                                   &OldProtect2);
1004                   if (!NT_SUCCESS(Status))
1005                     {
1006                       DPRINT1("Failed to protect relocation target2.\n");
1007                       return(Status);
1008                     }
1009             }
1010
1011           RelocationRVA += RelocationDir->SizeOfBlock;
1012           RelocationDir = 
1013             (PRELOCATION_DIRECTORY) (ImageBase + RelocationRVA);
1014         }
1015     }
1016   return STATUS_SUCCESS;
1017 }
1018
1019
1020 /**********************************************************************
1021  * NAME                                                         LOCAL
1022  *      LdrFixupImports
1023  *      
1024  * DESCRIPTION
1025  *      Compute the entry point for every symbol the DLL imports
1026  *      from other modules.
1027  *
1028  * ARGUMENTS
1029  *
1030  * RETURN VALUE
1031  *
1032  * REVISIONS
1033  *
1034  * NOTE
1035  *
1036  */
1037 static NTSTATUS LdrFixupImports(PIMAGE_NT_HEADERS       NTHeaders,
1038                                 PVOID                   ImageBase)
1039 {
1040    PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory;
1041    ULONG Ordinal;
1042    PVOID BaseAddress;
1043    NTSTATUS Status;
1044    ULONG IATSize;
1045    
1046    DPRINT("LdrFixupImports(NTHeaders %x, ImageBase %x)\n", NTHeaders, 
1047            ImageBase);
1048    
1049    /*
1050     * Process each import module.
1051     */
1052    ImportModuleDirectory = (PIMAGE_IMPORT_MODULE_DIRECTORY)(
1053                                ImageBase + NTHeaders->OptionalHeader
1054                                  .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1055                              .VirtualAddress);
1056    DPRINT("ImportModuleDirectory %x\n", ImportModuleDirectory);
1057
1058    while (ImportModuleDirectory->dwRVAModuleName)
1059      {
1060         PVOID   * ImportAddressList;
1061         PULONG  FunctionNameList;
1062         UNICODE_STRING DllName;
1063         DWORD   pName;
1064         WORD    pHint;
1065         PVOID   IATBase;
1066         ULONG   OldProtect;
1067
1068         DPRINT("ImportModule->Directory->dwRVAModuleName %s\n",
1069                (PCHAR)(ImageBase + ImportModuleDirectory->dwRVAModuleName));
1070
1071         RtlCreateUnicodeStringFromAsciiz (&DllName,
1072                   (PCHAR)(ImageBase + ImportModuleDirectory->dwRVAModuleName));
1073
1074         Status = LdrGetDllHandle (0, 0, &DllName, &BaseAddress);
1075         if (!NT_SUCCESS(Status))
1076           {
1077              Status = LdrLoadDll(NULL,
1078                                  0,
1079                                  &DllName,
1080                                  &BaseAddress);
1081              RtlFreeUnicodeString (&DllName);
1082              if (!NT_SUCCESS(Status))
1083                {
1084                   DbgPrint("LdrFixupImports:failed to load %s\n"
1085                         ,(PCHAR)(ImageBase 
1086                                 + ImportModuleDirectory->dwRVAModuleName));
1087
1088                   return Status;
1089                }
1090           }
1091
1092         /*
1093          * Get the import address list.
1094          */
1095         ImportAddressList = (PVOID *)(ImageBase
1096                         + ImportModuleDirectory->dwRVAFunctionAddressList);
1097         
1098         /*
1099          * Get the list of functions to import.
1100          */
1101         if (ImportModuleDirectory->dwRVAFunctionNameList != 0)
1102           {
1103              FunctionNameList = (PULONG) (
1104                                           ImageBase
1105                                           + ImportModuleDirectory->dwRVAFunctionNameList
1106                                           );
1107           }
1108         else
1109           {
1110              FunctionNameList = 
1111                (PULONG)(ImageBase 
1112                         + ImportModuleDirectory->dwRVAFunctionAddressList);
1113           }
1114
1115         /*
1116          * Get the size of IAT.
1117          */
1118         IATSize = 0;
1119         while (FunctionNameList[IATSize] != 0L)
1120           {
1121             IATSize++;
1122           }
1123
1124         /*
1125          * Unprotect the region we are about to write into.
1126          */
1127         IATBase = (PVOID)ImportAddressList;
1128         Status = NtProtectVirtualMemory(NtCurrentProcess(),
1129                                         IATBase,
1130                                         IATSize * sizeof(PVOID*),
1131                                         PAGE_READWRITE,
1132                                         &OldProtect);
1133         if (!NT_SUCCESS(Status))
1134           {
1135             DbgPrint("LDR: Failed to unprotect IAT.\n");
1136             return(Status);
1137           }
1138
1139         /*
1140          * Walk through function list and fixup addresses.
1141          */
1142         while (*FunctionNameList != 0L)
1143           {
1144              if ((*FunctionNameList) & 0x80000000)
1145                {
1146                   Ordinal = (*FunctionNameList) & 0x7fffffff;
1147                   *ImportAddressList = 
1148                     LdrGetExportByOrdinal(BaseAddress,
1149                                           Ordinal);
1150                }
1151              else
1152                {
1153                   pName = (DWORD) (ImageBase + *FunctionNameList + 2);
1154                   pHint = *(PWORD)(ImageBase + *FunctionNameList);
1155
1156                   *ImportAddressList = 
1157                     LdrGetExportByName(BaseAddress, (PUCHAR)pName, pHint);
1158                   if ((*ImportAddressList) == NULL)
1159                     {
1160                        DbgPrint("Failed to import %s\n", pName);
1161                        return STATUS_UNSUCCESSFUL;
1162                     }
1163                }
1164              ImportAddressList++;
1165              FunctionNameList++;
1166           }
1167
1168         /*
1169          * Protect the region we are about to write into.
1170          */
1171         Status = NtProtectVirtualMemory(NtCurrentProcess(),
1172                                         IATBase,
1173                                         IATSize * sizeof(PVOID*),
1174                                         OldProtect,
1175                                         &OldProtect);
1176         if (!NT_SUCCESS(Status))
1177           {
1178             DbgPrint("LDR: Failed to protect IAT.\n");
1179             return(Status);
1180           }
1181
1182         ImportModuleDirectory++;
1183      }
1184    return STATUS_SUCCESS;
1185 }
1186
1187
1188 /**********************************************************************
1189  * NAME
1190  *      LdrPEStartup
1191  *
1192  * DESCRIPTION
1193  *      1. Map the DLL's sections into memory.
1194  *      2. Relocate, if needed the DLL.
1195  *      3. Fixup any imported symbol.
1196  *      4. Compute the DLL's entry point.
1197  *
1198  * ARGUMENTS
1199  *      ImageBase
1200  *              Address at which the DLL's image
1201  *              is loaded.
1202  *              
1203  *      SectionHandle
1204  *              Handle of the section that contains
1205  *              the DLL's image.
1206  *
1207  * RETURN VALUE
1208  *      NULL on error; otherwise the entry point
1209  *      to call for initializing the DLL.
1210  *
1211  * REVISIONS
1212  *
1213  * NOTE
1214  *
1215  */
1216 PEPFUNC LdrPEStartup (PVOID  ImageBase,
1217                       HANDLE SectionHandle,
1218                       PLDR_MODULE* Module,
1219                       PWSTR FullDosName)
1220 {
1221    NTSTATUS             Status;
1222    PEPFUNC              EntryPoint = NULL;
1223    PIMAGE_DOS_HEADER    DosHeader;
1224    PIMAGE_NT_HEADERS    NTHeaders;
1225
1226    DPRINT("LdrPEStartup(ImageBase %x SectionHandle %x)\n",
1227            ImageBase, (ULONG)SectionHandle);
1228
1229    /*
1230     * Overlay DOS and WNT headers structures
1231     * to the DLL's image.
1232     */
1233    DosHeader = (PIMAGE_DOS_HEADER) ImageBase;
1234    NTHeaders = (PIMAGE_NT_HEADERS) (ImageBase + DosHeader->e_lfanew);
1235
1236    /*
1237     * If the base address is different from the
1238     * one the DLL is actually loaded, perform any
1239     * relocation.
1240     */
1241    if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase)
1242      {
1243        DbgPrint("LDR: Performing relocations\n");
1244        Status = LdrPerformRelocations(NTHeaders, ImageBase);
1245        if (!NT_SUCCESS(Status))
1246          {
1247            DbgPrint("LdrPerformRelocations() failed\n");
1248            return NULL;
1249          }
1250      }
1251
1252    if (Module != NULL)
1253      {
1254        *Module = LdrAddModuleEntry(ImageBase, NTHeaders, FullDosName);
1255      }
1256
1257    /*
1258     * If the DLL's imports symbols from other
1259     * modules, fixup the imported calls entry points.
1260     */
1261    if (NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1262        .VirtualAddress != 0)
1263      {
1264         DPRINT("About to fixup imports\n");
1265         Status = LdrFixupImports(NTHeaders, ImageBase);
1266         if (!NT_SUCCESS(Status))
1267           {
1268              DbgPrint("LdrFixupImports() failed\n");
1269              return NULL;
1270           }
1271         DPRINT("Fixup done\n");
1272      }
1273
1274    /*
1275     * Compute the DLL's entry point's address.
1276     */
1277    DPRINT("ImageBase = %x\n",(ULONG)ImageBase);
1278    DPRINT("AddressOfEntryPoint = %x\n",(ULONG)NTHeaders->OptionalHeader.AddressOfEntryPoint);
1279    if (NTHeaders->OptionalHeader.AddressOfEntryPoint != 0)
1280      {
1281         EntryPoint = (PEPFUNC) (ImageBase
1282                            + NTHeaders->OptionalHeader.AddressOfEntryPoint);
1283      }
1284    DPRINT("LdrPEStartup() = %x\n",EntryPoint);
1285    return EntryPoint;
1286 }
1287
1288
1289 NTSTATUS STDCALL
1290 LdrUnloadDll (IN PVOID BaseAddress)
1291 {
1292    PIMAGE_NT_HEADERS NtHeaders;
1293    PDLLMAIN_FUNC Entrypoint;
1294    PLIST_ENTRY ModuleListHead;
1295    PLIST_ENTRY Entry;
1296    PLDR_MODULE Module;
1297    NTSTATUS Status;
1298
1299    if (BaseAddress == NULL)
1300      return STATUS_SUCCESS;
1301
1302    ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1303    Entry = ModuleListHead->Flink;
1304
1305    while (Entry != ModuleListHead)
1306      {
1307         Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
1308         if (Module->BaseAddress == BaseAddress)
1309           {
1310              if (Module->LoadCount == -1)
1311                {
1312                   /* never unload this dll */
1313                   return STATUS_SUCCESS;
1314                }
1315              else if (Module->LoadCount > 1)
1316                {
1317                   Module->LoadCount--;
1318                   return STATUS_SUCCESS;
1319                }
1320
1321              NtHeaders = RtlImageNtHeader (Module->BaseAddress);
1322              if ((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == IMAGE_FILE_DLL)
1323                {
1324                   if (Module->EntryPoint != 0)
1325                     {
1326                        Entrypoint = (PDLLMAIN_FUNC)Module->EntryPoint;
1327                        DPRINT("Calling entry point at 0x%08x\n", Entrypoint);
1328                        Entrypoint(Module->BaseAddress,
1329                                   DLL_PROCESS_DETACH,
1330                                   NULL);
1331                     }
1332                   else
1333                     {
1334                        DPRINT("NTDLL.LDR: Entrypoint is NULL for \n");
1335                     }
1336                }
1337              Status = ZwUnmapViewOfSection (NtCurrentProcess (),
1338                                             Module->BaseAddress);
1339              ZwClose (Module->SectionHandle);
1340
1341              /* remove the module entry from the list */
1342              RtlFreeUnicodeString (&Module->FullDllName);
1343              RtlFreeUnicodeString (&Module->BaseDllName);
1344              RemoveEntryList (Entry);
1345              RtlFreeHeap (RtlGetProcessHeap (), 0, Module);
1346
1347              return Status;
1348           }
1349
1350         Entry = Entry->Flink;
1351      }
1352
1353    DPRINT("NTDLL.LDR: Dll not found\n")
1354
1355    return STATUS_UNSUCCESSFUL;
1356 }
1357
1358 #if 0 /*MOVED_TO_FILE_RES_C*/
1359
1360 NTSTATUS STDCALL
1361 LdrFindResource_U(PVOID BaseAddress,
1362                   PLDR_RESOURCE_INFO ResourceInfo,
1363                   ULONG Level,
1364                   PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry)
1365 {
1366    PIMAGE_RESOURCE_DIRECTORY ResDir;
1367    PIMAGE_RESOURCE_DIRECTORY ResBase;
1368    PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry;
1369    NTSTATUS Status = STATUS_SUCCESS;
1370    ULONG EntryCount;
1371    PWCHAR ws;
1372    ULONG i;
1373    ULONG Id;
1374
1375    DPRINT ("LdrFindResource_U()\n");
1376
1377    /* Get the pointer to the resource directory */
1378    ResDir = (PIMAGE_RESOURCE_DIRECTORY)
1379         RtlImageDirectoryEntryToData (BaseAddress,
1380                                       TRUE,
1381                                       IMAGE_DIRECTORY_ENTRY_RESOURCE,
1382                                       &i);
1383    if (ResDir == NULL)
1384      {
1385         return STATUS_RESOURCE_DATA_NOT_FOUND;
1386      }
1387
1388    DPRINT("ResourceDirectory: %x\n", (ULONG)ResDir);
1389
1390    ResBase = ResDir;
1391
1392    /* Let's go into resource tree */
1393    for (i = 0; i < Level; i++)
1394      {
1395         DPRINT("ResDir: %x\n", (ULONG)ResDir);
1396         Id = ((PULONG)ResourceInfo)[i];
1397         EntryCount = ResDir->NumberOfNamedEntries;
1398         ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1);
1399         DPRINT("ResEntry %x\n", (ULONG)ResEntry);
1400         if (Id & 0xFFFF0000)
1401           {
1402              /* Resource name is a unicode string */
1403              for (; EntryCount--; ResEntry++)
1404                {
1405                   /* Scan entries for equal name */
1406                   if (ResEntry->Name & 0x80000000)
1407                     {
1408                        ws = (PWCHAR)((ULONG)ResDir + (ResEntry->Name & 0x7FFFFFFF));
1409                        if (!wcsncmp((PWCHAR)Id, ws + 1, *ws ) &&
1410                            wcslen((PWCHAR)Id) == (int)*ws )
1411                          {
1412                             goto found;
1413                          }
1414                     }
1415                }
1416           }
1417         else
1418           {
1419              /* We use ID number instead of string */
1420              ResEntry += EntryCount;
1421              EntryCount = ResDir->NumberOfIdEntries;
1422              for (; EntryCount--; ResEntry++)
1423                {
1424                   /* Scan entries for equal name */
1425                   if (ResEntry->Name == Id)
1426                     {
1427                      DPRINT("ID entry found %x\n", Id);
1428                      goto found;
1429                     }
1430                }
1431           }
1432         DPRINT("Error %lu\n", i);
1433
1434           switch (i)
1435           {
1436              case 0:
1437                 return STATUS_RESOURCE_TYPE_NOT_FOUND;
1438
1439              case 1:
1440                 return STATUS_RESOURCE_NAME_NOT_FOUND;
1441
1442              case 2:
1443                 if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries)
1444                   {
1445                      /* Use the first available language */
1446                      ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1);
1447                      break;
1448                   }
1449                 return STATUS_RESOURCE_LANG_NOT_FOUND;
1450
1451              case 3:
1452                 return STATUS_RESOURCE_DATA_NOT_FOUND;
1453
1454              default:
1455                 return STATUS_INVALID_PARAMETER;
1456           }
1457 found:;
1458         ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResBase +
1459                 (ResEntry->OffsetToData & 0x7FFFFFFF));
1460      }
1461    DPRINT("ResourceDataEntry: %x\n", (ULONG)ResDir);
1462
1463    if (ResourceDataEntry)
1464      {
1465         *ResourceDataEntry = (PVOID)ResDir;
1466      }
1467
1468   return Status;
1469 }
1470
1471
1472 NTSTATUS STDCALL
1473 LdrAccessResource(IN  PVOID BaseAddress,
1474                   IN  PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
1475                   OUT PVOID *Resource OPTIONAL,
1476                   OUT PULONG Size OPTIONAL)
1477 {
1478    PIMAGE_SECTION_HEADER Section;
1479    PIMAGE_NT_HEADERS NtHeader;
1480    ULONG SectionRva;
1481    ULONG SectionVa;
1482    ULONG DataSize;
1483    ULONG Offset = 0;
1484    ULONG Data;
1485
1486    Data = (ULONG)RtlImageDirectoryEntryToData (BaseAddress,
1487                                                TRUE,
1488                                                IMAGE_DIRECTORY_ENTRY_RESOURCE,
1489                                                &DataSize);
1490    if (Data == 0)
1491         return STATUS_RESOURCE_DATA_NOT_FOUND;
1492
1493    if ((ULONG)BaseAddress & 1)
1494      {
1495         /* loaded as ordinary file */
1496         NtHeader = RtlImageNtHeader((PVOID)((ULONG)BaseAddress & ~1UL));
1497         Offset = (ULONG)BaseAddress - Data + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
1498         Section = RtlImageRvaToSection (NtHeader, BaseAddress, NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
1499         if (Section == NULL)
1500           {
1501              return STATUS_RESOURCE_DATA_NOT_FOUND;
1502           }
1503
1504         if (Section->Misc.VirtualSize < ResourceDataEntry->OffsetToData)
1505           {
1506              SectionRva = RtlImageRvaToSection (NtHeader, BaseAddress, ResourceDataEntry->OffsetToData)->VirtualAddress;
1507              SectionVa = RtlImageRvaToVa(NtHeader, BaseAddress, SectionRva, NULL);
1508              Offset = SectionRva - SectionVa + Data - Section->VirtualAddress;
1509           }
1510      }
1511
1512    if (Resource)
1513      {
1514         *Resource = (PVOID)(ResourceDataEntry->OffsetToData - Offset + (ULONG)BaseAddress);
1515      }
1516
1517    if (Size)
1518      {
1519         *Size = ResourceDataEntry->Size;
1520      }
1521
1522    return STATUS_SUCCESS;
1523 }
1524
1525 #endif /*MOVED_TO_FILE_RES_C*/
1526
1527 NTSTATUS STDCALL
1528 LdrDisableThreadCalloutsForDll(IN PVOID BaseAddress)
1529 {
1530     PLIST_ENTRY ModuleListHead;
1531     PLIST_ENTRY Entry;
1532     PLDR_MODULE Module;
1533     NTSTATUS Status;
1534
1535     DPRINT("LdrDisableThreadCalloutsForDll (BaseAddress %x)\n", BaseAddress);
1536
1537     Status = STATUS_DLL_NOT_FOUND;
1538     ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1539     Entry = ModuleListHead->Flink;
1540     while (Entry != ModuleListHead) {
1541         Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
1542
1543         DPRINT("BaseDllName %wZ BaseAddress %x\n", &Module->BaseDllName, Module->BaseAddress);
1544
1545         if (Module->BaseAddress == BaseAddress) {
1546             if (Module->TlsIndex == 0) {
1547                 Module->Flags |= 0x00040000;
1548                 Status = STATUS_SUCCESS;
1549             }
1550             return Status;
1551         }
1552         Entry = Entry->Flink;
1553     }
1554     return Status;
1555 }
1556
1557 #if 0 /*MOVED_TO_FILE_RES_C*/
1558
1559 NTSTATUS STDCALL
1560 LdrFindResourceDirectory_U (IN PVOID BaseAddress,
1561                             WCHAR **name,
1562                             DWORD level,
1563                             OUT PVOID *addr)
1564 {
1565    PIMAGE_RESOURCE_DIRECTORY ResDir;
1566    PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry;
1567    ULONG EntryCount;
1568    ULONG i;
1569    NTSTATUS Status = STATUS_SUCCESS;
1570    WCHAR *ws;
1571
1572    /* Get the pointer to the resource directory */
1573    ResDir = (PIMAGE_RESOURCE_DIRECTORY)
1574         RtlImageDirectoryEntryToData (BaseAddress,
1575                                       TRUE,
1576                                       IMAGE_DIRECTORY_ENTRY_RESOURCE,
1577                                       &i);
1578    if (ResDir == NULL)
1579      {
1580         return STATUS_RESOURCE_DATA_NOT_FOUND;
1581      }
1582
1583    /* Let's go into resource tree */
1584    for (i = 0; i < level; i++, name++)
1585      {
1586         EntryCount = ResDir->NumberOfNamedEntries;
1587         ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1);
1588         if ((ULONG)(*name) & 0xFFFF0000)
1589           {
1590              /* Resource name is a unicode string */
1591              for (; EntryCount--; ResEntry++)
1592                {
1593                   /* Scan entries for equal name */
1594                   if (ResEntry->Name & 0x80000000)
1595                     {
1596                        ws = (WCHAR*)((ULONG)ResDir + (ResEntry->Name & 0x7FFFFFFF));
1597                        if (!wcsncmp( *name, ws + 1, *ws ) && wcslen( *name ) == (int)*ws )
1598                          {
1599                             goto found;
1600                          }
1601                     }
1602                }
1603           }
1604         else
1605           {
1606              /* We use ID number instead of string */
1607              ResEntry += EntryCount;
1608              EntryCount = ResDir->NumberOfIdEntries;
1609              for (; EntryCount--; ResEntry++)
1610                {
1611                   /* Scan entries for equal name */
1612                   if (ResEntry->Name == (ULONG)(*name))
1613                      goto found;
1614                }
1615           }
1616
1617           switch (i)
1618           {
1619              case 0:
1620                 return STATUS_RESOURCE_TYPE_NOT_FOUND;
1621
1622              case 1:
1623                 return STATUS_RESOURCE_NAME_NOT_FOUND;
1624
1625              case 2:
1626                 Status = STATUS_RESOURCE_LANG_NOT_FOUND;
1627                 /* Just use first language entry */
1628                 if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries)
1629                   {
1630                      ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1);
1631                      break;
1632                   }
1633                 return Status;
1634
1635              case 3:
1636                 return STATUS_RESOURCE_DATA_NOT_FOUND;
1637
1638              default:
1639                 return STATUS_INVALID_PARAMETER;
1640           }
1641 found:;
1642         ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResDir + ResEntry->OffsetToData);
1643      }
1644
1645    if (addr)
1646      {
1647         *addr = (PVOID)ResDir;
1648      }
1649
1650   return Status;
1651 }
1652
1653 #endif /*MOVED_TO_FILE_RES_C*/
1654
1655 NTSTATUS
1656 STDCALL
1657 LdrGetDllHandle(IN ULONG Unknown1,
1658                 IN ULONG Unknown2,
1659                 IN PUNICODE_STRING DllName,
1660                 OUT PVOID* BaseAddress)
1661 {
1662     UNICODE_STRING FullDllName;
1663     PLIST_ENTRY ModuleListHead;
1664     PLIST_ENTRY Entry;
1665     PLDR_MODULE Module;
1666
1667     DPRINT("LdrGetDllHandle (Unknown1 %x Unknown2 %x DllName %wZ BaseAddress %p)\n",
1668            Unknown1, Unknown2, DllName, BaseAddress);
1669
1670     /* NULL is the current executable */
1671     if (DllName == NULL) {
1672         *BaseAddress = NtCurrentPeb()->ImageBaseAddress;
1673         DPRINT("BaseAddress %x\n", *BaseAddress);
1674         return STATUS_SUCCESS;
1675     }
1676     LdrAdjustDllName(&FullDllName, DllName, TRUE);
1677
1678     DPRINT("FullDllName %wZ\n", &FullDllName);
1679
1680     ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1681     Entry = ModuleListHead->Flink;
1682     while (Entry != ModuleListHead) {
1683         Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
1684
1685         DPRINT("EntryPoint %x\n", Module->EntryPoint);
1686         DPRINT("Comparing %wZ and %wZ\n", &Module->BaseDllName, &FullDllName);
1687
1688         if (!RtlCompareUnicodeString(&Module->BaseDllName, &FullDllName, TRUE)) {
1689              RtlFreeUnicodeString(&FullDllName);
1690              *BaseAddress = Module->BaseAddress;
1691              DPRINT("BaseAddress %x\n", *BaseAddress);
1692              return STATUS_SUCCESS;
1693         }
1694         Entry = Entry->Flink;
1695     }
1696
1697     DPRINT("Failed to find dll %wZ\n", &FullDllName);
1698
1699     RtlFreeUnicodeString(&FullDllName);
1700     *BaseAddress = NULL;
1701     return STATUS_DLL_NOT_FOUND;
1702 }
1703
1704
1705 NTSTATUS STDCALL
1706 LdrGetProcedureAddress (IN PVOID BaseAddress,
1707                         IN PANSI_STRING Name,
1708                         IN ULONG Ordinal,
1709                         OUT PVOID *ProcedureAddress)
1710 {
1711    PIMAGE_EXPORT_DIRECTORY ExportDir;
1712    PUSHORT OrdinalPtr;
1713    PULONG NamePtr;
1714    PULONG AddressPtr;
1715    ULONG i = 0;
1716
1717    DPRINT("LdrGetProcedureAddress (BaseAddress %x Name %Z Ordinal %lu ProcedureAddress %x)\n",
1718           BaseAddress, Name, Ordinal, ProcedureAddress);
1719
1720    /* Get the pointer to the export directory */
1721    ExportDir = (PIMAGE_EXPORT_DIRECTORY)
1722                 RtlImageDirectoryEntryToData (BaseAddress,
1723                                               TRUE,
1724                                               IMAGE_DIRECTORY_ENTRY_EXPORT,
1725                                               &i);
1726
1727    DPRINT("ExportDir %x i %lu\n", ExportDir, i);
1728
1729    if (!ExportDir || !i || !ProcedureAddress)
1730      {
1731         return STATUS_INVALID_PARAMETER;
1732      }
1733
1734    AddressPtr = (PULONG)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfFunctions);
1735    if (Name && Name->Length)
1736      {
1737         /* by name */
1738         OrdinalPtr = (PUSHORT)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfNameOrdinals);
1739         NamePtr = (PULONG)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfNames);
1740         for( i = 0; i < ExportDir->NumberOfNames; i++, NamePtr++, OrdinalPtr++)
1741           {
1742              if (!_strnicmp(Name->Buffer, (char*)(BaseAddress + *NamePtr), Name->Length))
1743                {
1744                   *ProcedureAddress = (PVOID)((ULONG)BaseAddress + (ULONG)AddressPtr[*OrdinalPtr]);
1745                   return STATUS_SUCCESS;
1746                }
1747           }
1748         DPRINT("LdrGetProcedureAddress: Can't resolve symbol '%Z'\n", Name);
1749      }
1750    else
1751      {
1752         /* by ordinal */
1753         Ordinal &= 0x0000FFFF;
1754         if (Ordinal - ExportDir->Base < ExportDir->NumberOfFunctions)
1755           {
1756              *ProcedureAddress = (PVOID)((ULONG)BaseAddress + (ULONG)AddressPtr[Ordinal - ExportDir->Base]);
1757              return STATUS_SUCCESS;
1758           }
1759         DPRINT("LdrGetProcedureAddress: Can't resolve symbol @%d\n", Ordinal);
1760   }
1761
1762    return STATUS_PROCEDURE_NOT_FOUND;
1763 }
1764
1765
1766 NTSTATUS STDCALL
1767 LdrShutdownProcess (VOID)
1768 {
1769    PLIST_ENTRY ModuleListHead;
1770    PLIST_ENTRY Entry;
1771    PLDR_MODULE Module;
1772
1773    DPRINT("LdrShutdownProcess() called\n");
1774
1775    RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock);
1776
1777    ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
1778    Entry = ModuleListHead->Blink;
1779
1780    while (Entry != ModuleListHead)
1781      {
1782         Module = CONTAINING_RECORD(Entry, LDR_MODULE, InInitializationOrderModuleList);
1783
1784         DPRINT("  Unloading %wZ\n",
1785                &Module->BaseDllName);
1786         // PJS: only detach from static dlls, they should FreeLibrary() any dlls that
1787         // they loaded dynamically, and when the last reference is gone, that lib will
1788         // be detached.  
1789         if (Module->EntryPoint != 0 && Module->LoadCount == -1)
1790           {
1791              PDLLMAIN_FUNC Entrypoint = (PDLLMAIN_FUNC)Module->EntryPoint;
1792
1793              DPRINT("Calling entry point at 0x%08x\n", Entrypoint);
1794              Entrypoint (Module->BaseAddress,
1795                          DLL_PROCESS_DETACH,
1796                          NULL);
1797           }
1798
1799         Entry = Entry->Blink;
1800      }
1801
1802    RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock);
1803
1804    DPRINT("LdrShutdownProcess() done\n");
1805
1806    return STATUS_SUCCESS;
1807 }
1808
1809
1810 NTSTATUS STDCALL
1811 LdrShutdownThread (VOID)
1812 {
1813    PLIST_ENTRY ModuleListHead;
1814    PLIST_ENTRY Entry;
1815    PLDR_MODULE Module;
1816
1817    DPRINT("LdrShutdownThread() called\n");
1818
1819    RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock);
1820
1821    ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
1822    Entry = ModuleListHead->Blink;
1823
1824    while (Entry != ModuleListHead)
1825      {
1826         Module = CONTAINING_RECORD(Entry, LDR_MODULE, InInitializationOrderModuleList);
1827
1828         DPRINT("  Unloading %wZ\n",
1829                &Module->BaseDllName);
1830
1831         if (Module->EntryPoint != 0)
1832           {
1833              PDLLMAIN_FUNC Entrypoint = (PDLLMAIN_FUNC)Module->EntryPoint;
1834
1835              DPRINT("Calling entry point at 0x%08x\n", Entrypoint);
1836              Entrypoint (Module->BaseAddress,
1837                          DLL_THREAD_DETACH,
1838                          NULL);
1839           }
1840
1841         Entry = Entry->Blink;
1842      }
1843
1844    RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock);
1845
1846    DPRINT("LdrShutdownThread() done\n");
1847
1848    return STATUS_SUCCESS;
1849 }
1850
1851
1852 /***************************************************************************
1853  * NAME                                                         EXPORTED
1854  *      LdrQueryProcessModuleInformation
1855  *
1856  * DESCRIPTION
1857  *
1858  * ARGUMENTS
1859  *
1860  * RETURN VALUE
1861  *
1862  * REVISIONS
1863  *
1864  * NOTE
1865  */
1866 NTSTATUS STDCALL
1867 LdrQueryProcessModuleInformation(IN PMODULE_INFORMATION ModuleInformation OPTIONAL,
1868                                  IN ULONG Size OPTIONAL,
1869                                  OUT PULONG ReturnedSize)
1870
1871 {
1872   PLIST_ENTRY ModuleListHead;
1873   PLIST_ENTRY Entry;
1874   PLDR_MODULE Module;
1875   PMODULE_ENTRY ModulePtr = NULL;
1876   NTSTATUS Status = STATUS_SUCCESS;
1877   ULONG UsedSize = sizeof(ULONG);
1878   ANSI_STRING AnsiString;
1879   PCHAR p;
1880
1881   DPRINT("LdrQueryProcessModuleInformation() called\n");
1882
1883   RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock);
1884
1885   if (ModuleInformation == NULL || Size == 0)
1886     {
1887       Status = STATUS_INFO_LENGTH_MISMATCH;
1888     }
1889   else
1890     {
1891       ModuleInformation->ModuleCount = 0;
1892       ModulePtr = &ModuleInformation->ModuleEntry[0];
1893       Status = STATUS_SUCCESS;
1894     }
1895
1896   ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1897   Entry = ModuleListHead->Flink;
1898
1899   while (Entry != ModuleListHead)
1900     {
1901       Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
1902
1903       DPRINT("  Module %wZ\n",
1904              &Module->FullDllName);
1905
1906       if (UsedSize > Size)
1907         {
1908           Status = STATUS_INFO_LENGTH_MISMATCH;
1909         }
1910       else if (ModuleInformation != NULL)
1911         {
1912           ModulePtr->Unknown0 = 0;      // FIXME: ??
1913           ModulePtr->Unknown1 = 0;      // FIXME: ??
1914           ModulePtr->BaseAddress = Module->BaseAddress;
1915           ModulePtr->SizeOfImage = Module->SizeOfImage;
1916           ModulePtr->Flags = Module->Flags;
1917           ModulePtr->Unknown2 = 0;      // FIXME: load order index ??
1918           ModulePtr->Unknown3 = 0;      // FIXME: ??
1919           ModulePtr->LoadCount = Module->LoadCount;
1920
1921           AnsiString.Length = 0;
1922           AnsiString.MaximumLength = 256;
1923           AnsiString.Buffer = ModulePtr->ModuleName;
1924           RtlUnicodeStringToAnsiString(&AnsiString,
1925                                        &Module->FullDllName,
1926                                        FALSE);
1927           p = strrchr(ModulePtr->ModuleName, '\\');
1928           if (p != NULL)
1929             ModulePtr->PathLength = p - ModulePtr->ModuleName + 1;
1930           else
1931             ModulePtr->PathLength = 0;
1932
1933           ModulePtr++;
1934           ModuleInformation->ModuleCount++;
1935         }
1936       UsedSize += sizeof(MODULE_ENTRY);
1937
1938       Entry = Entry->Flink;
1939     }
1940
1941   RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock);
1942
1943   if (ReturnedSize != 0)
1944     *ReturnedSize = UsedSize;
1945
1946   DPRINT("LdrQueryProcessModuleInformation() done\n");
1947
1948   return(Status);
1949 }
1950
1951 /* EOF */