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