6e9d3a1b4c9bae27674a6786e0d5230391f7e310
[reactos.git] / ntoskrnl / ldr / init.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001, 2002 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /* $Id$
20  *
21  * PROJECT:         ReactOS kernel
22  * FILE:            ntoskrnl/ldr/init.c
23  * PURPOSE:         Loaders for PE executables
24  * PROGRAMMERS:     Jean Michault
25  *                  Rex Jolliff (rex@lvcablemodem.com)
26  * UPDATE HISTORY:
27  *   DW   22/05/98  Created
28  *   RJJ  10/12/98  Completed image loader function and added hooks for MZ/PE
29  *   RJJ  10/12/98  Built driver loader function and added hooks for PE/COFF
30  *   RJJ  10/12/98  Rolled in David's code to load COFF drivers
31  *   JM   14/12/98  Built initial PE user module loader
32  *   RJJ  06/03/99  Moved user PE loader into NTDLL
33  *   EA   19990717  LdrGetSystemDirectory()
34  *   EK   20000618  Using SystemRoot link instead of LdrGetSystemDirectory()
35  *   EK   20021119  Create a process parameter block for the initial process.
36  */
37
38 /* INCLUDES *****************************************************************/
39
40 #include <ddk/ntddk.h>
41 #include <internal/i386/segment.h>
42 #include <internal/module.h>
43 #include <internal/ntoskrnl.h>
44 #include <internal/ob.h>
45 #include <internal/ps.h>
46 #include <internal/ldr.h>
47 #include <napi/teb.h>
48
49 #define NDEBUG
50 #include <internal/debug.h>
51
52
53 /* MACROS ******************************************************************/
54
55 #define DENORMALIZE(x,addr) {if(x) x=(VOID*)((ULONG)(x)-(ULONG)(addr));}
56 #define ALIGN(x,align)      (((ULONG)(x)+(align)-1UL)&(~((align)-1UL)))
57
58
59 /* FUNCTIONS *****************************************************************/
60
61 static NTSTATUS
62 LdrpMapProcessImage(PHANDLE SectionHandle,
63                     PUNICODE_STRING ImagePath)
64 {
65   OBJECT_ATTRIBUTES ObjectAttributes;
66   HANDLE FileHandle;
67   NTSTATUS Status;
68
69   /* Open image file */
70   InitializeObjectAttributes(&ObjectAttributes,
71                              ImagePath,
72                              0,
73                              NULL,
74                              NULL);
75
76   DPRINT("Opening image file %S\n", ObjectAttributes.ObjectName->Buffer);
77   Status = NtOpenFile(&FileHandle,
78                       FILE_ALL_ACCESS,
79                       &ObjectAttributes,
80                       NULL,
81                       0,
82                       0);
83   if (!NT_SUCCESS(Status))
84     {
85       DPRINT("NtOpenFile() failed (Status %lx)\n", Status);
86       return(Status);
87     }
88
89   /* Create a section for the image */
90   DPRINT("Creating section\n");
91   Status = NtCreateSection(SectionHandle,
92                            SECTION_ALL_ACCESS,
93                            NULL,
94                            NULL,
95                            PAGE_READWRITE,
96                            SEC_COMMIT | SEC_IMAGE,
97                            FileHandle);
98   NtClose(FileHandle);
99   if (!NT_SUCCESS(Status))
100     {
101       DPRINT("NtCreateSection() failed (Status %lx)\n", Status);
102     }
103
104   return(Status);
105 }
106
107
108 static NTSTATUS
109 LdrpCreateProcessEnvironment(HANDLE ProcessHandle,
110                              PUNICODE_STRING ImagePath,
111                              PVOID* ImageBaseAddress)
112 {
113   PRTL_USER_PROCESS_PARAMETERS LocalPpb;
114   PRTL_USER_PROCESS_PARAMETERS ProcessPpb;
115   ULONG BytesWritten;
116   ULONG Offset;
117   ULONG Size;
118   ULONG RegionSize;
119   NTSTATUS Status;
120
121   /* Calculate the PPB size */
122   Size = sizeof(RTL_USER_PROCESS_PARAMETERS);
123   Size += ALIGN(ImagePath->Length + sizeof(WCHAR), sizeof(ULONG));
124   RegionSize = ROUND_UP(Size, PAGE_SIZE);
125   DPRINT("Size %lu  RegionSize %lu\n", Size, RegionSize);
126
127   /* Allocate the local PPB */
128   LocalPpb = NULL;
129   Status = NtAllocateVirtualMemory(NtCurrentProcess(),
130                                    (PVOID*)&LocalPpb,
131                                    0,
132                                    &RegionSize,
133                                    MEM_RESERVE | MEM_COMMIT,
134                                    PAGE_READWRITE);
135   if (!NT_SUCCESS(Status))
136     {
137       DPRINT("NtAllocateVirtualMemory() failed (Status %lx)\n", Status);
138       return(Status);
139     }
140
141   DPRINT("LocalPpb %p  AllocationSize %lu\n", LocalPpb, RegionSize);
142
143   /* Initialize the local PPB */
144   RtlZeroMemory(LocalPpb,
145                 RegionSize);
146   LocalPpb->AllocationSize = RegionSize;
147   LocalPpb->Size = Size;
148   LocalPpb->ImagePathName.Length = ImagePath->Length;
149   LocalPpb->ImagePathName.MaximumLength = ImagePath->Length + sizeof(WCHAR);
150   LocalPpb->ImagePathName.Buffer = (PWCHAR)(LocalPpb + 1);
151
152   /* Copy image path */
153   RtlCopyMemory(LocalPpb->ImagePathName.Buffer,
154                 ImagePath->Buffer,
155                 ImagePath->Length);
156   LocalPpb->ImagePathName.Buffer[ImagePath->Length / sizeof(WCHAR)] = (WCHAR)0;
157
158   /* Denormalize the process parameter block */
159   DENORMALIZE(LocalPpb->ImagePathName.Buffer, LocalPpb);
160   LocalPpb->Flags &= ~PPF_NORMALIZED;
161
162   /* Create the process PPB */
163   ProcessPpb = NULL;
164   Status = NtAllocateVirtualMemory(ProcessHandle,
165                                    (PVOID*)&ProcessPpb,
166                                    0,
167                                    &RegionSize,
168                                    MEM_RESERVE | MEM_COMMIT,
169                                    PAGE_READWRITE);
170   if (!NT_SUCCESS(Status))
171     {
172       DPRINT("NtAllocateVirtualMemory() failed (Status %lx)\n", Status);
173
174       /* Release the local PPB */
175       RegionSize = 0;
176       NtFreeVirtualMemory(NtCurrentProcess(),
177                           (PVOID*)&LocalPpb,
178                           &RegionSize,
179                           MEM_RELEASE);
180       return(Status);
181     }
182
183   /* Copy local PPB into the process PPB */
184   NtWriteVirtualMemory(ProcessHandle,
185                        ProcessPpb,
186                        LocalPpb,
187                        LocalPpb->AllocationSize,
188                        &BytesWritten);
189
190   /* Update pointer to process PPB in the process PEB */
191   Offset = FIELD_OFFSET(PEB, ProcessParameters);
192   NtWriteVirtualMemory(ProcessHandle,
193                        (PVOID)(PEB_BASE + Offset),
194                        &ProcessPpb,
195                        sizeof(ProcessPpb),
196                        &BytesWritten);
197
198   /* Release local PPB */
199   RegionSize = 0;
200   NtFreeVirtualMemory(NtCurrentProcess(),
201                       (PVOID*)&LocalPpb,
202                       &RegionSize,
203                       MEM_RELEASE);
204
205   /* Set image file name */
206   Status = NtSetInformationProcess(ProcessHandle,
207                                    ProcessImageFileName,
208                                    "SMSS",
209                                    5);
210   if (!NT_SUCCESS(Status))
211     {
212       DPRINT("NtSetInformationProcess() failed (Status %lx)\n", Status);
213       return(Status);
214     }
215
216   /* Read image base address. */
217   Offset = FIELD_OFFSET(PEB, ImageBaseAddress);
218   NtReadVirtualMemory(ProcessHandle,
219                       (PVOID)(PEB_BASE + Offset),
220                       ImageBaseAddress,
221                       sizeof(PVOID),
222                       &BytesWritten);
223
224   return(STATUS_SUCCESS);
225 }
226
227 /*
228  FIXME: this sucks. Sucks sucks sucks. This code was duplicated, if you can
229  believe it, in four different places - excluding this, and twice in the two
230  DLLs that contained it (kernel32.dll and ntdll.dll). As much as I'd like to
231  rip the whole RTL out of ntdll.dll and ntoskrnl.exe and into its own static
232  library, ntoskrnl.exe is built separatedly from the rest of ReactOS, coming
233  with its own linker scripts and specifications, and, save for changes and fixes
234  to make it at least compile, I'm not going to touch any of it. If you feel
235  brave enough, you're welcome [KJK::Hyperion]
236 */
237 static NTSTATUS LdrpCreateStack
238 (
239  HANDLE ProcessHandle,
240  PUSER_STACK UserStack,
241  PULONG_PTR StackReserve,
242  PULONG_PTR StackCommit
243 )
244 {
245  PVOID pStackLowest = NULL;
246  ULONG_PTR nSize = 0;
247  NTSTATUS nErrCode;
248
249  if(StackReserve == NULL || StackCommit == NULL)
250   return STATUS_INVALID_PARAMETER;
251
252  /* FIXME: no SEH, no guard pages */
253  *StackCommit = *StackReserve;
254
255  UserStack->FixedStackBase = NULL;
256  UserStack->FixedStackLimit =  NULL;
257  UserStack->ExpandableStackBase = NULL;
258  UserStack->ExpandableStackLimit = NULL; 
259  UserStack->ExpandableStackBottom = NULL;
260
261  /* FIXME: this code assumes a stack growing downwards */
262  /* fixed stack */
263  if(*StackCommit == *StackReserve)
264  {
265   DPRINT("Fixed stack\n");
266
267   UserStack->FixedStackLimit = NULL;
268
269   /* allocate the stack */
270   nErrCode = NtAllocateVirtualMemory
271   (
272    ProcessHandle,
273    &(UserStack->FixedStackLimit),
274    0,
275    StackReserve,
276    MEM_RESERVE | MEM_COMMIT,
277    PAGE_READWRITE
278   );
279
280   /* failure */
281   if(!NT_SUCCESS(nErrCode)) return nErrCode;
282
283   /* store the highest (first) address of the stack */
284   UserStack->FixedStackBase =
285    (PUCHAR)(UserStack->FixedStackLimit) + *StackReserve;
286  }
287  /* expandable stack */
288  else
289  {
290   ULONG_PTR nGuardSize = PAGE_SIZE;
291   PVOID pGuardBase;
292
293   DPRINT("Expandable stack\n");
294
295   UserStack->FixedStackLimit = NULL;
296   UserStack->FixedStackBase = NULL;
297   UserStack->ExpandableStackBottom = NULL;
298
299   /* reserve the stack */
300   nErrCode = NtAllocateVirtualMemory
301   (
302    ProcessHandle,
303    &(UserStack->ExpandableStackBottom),
304    0,
305    StackReserve,
306    MEM_RESERVE,
307    PAGE_READWRITE
308   );
309
310   /* failure */
311   if(!NT_SUCCESS(nErrCode)) return nErrCode;
312
313   DPRINT("Reserved %08X bytes\n", *StackReserve);
314
315   /* expandable stack base - the highest address of the stack */
316   UserStack->ExpandableStackBase =
317    (PUCHAR)(UserStack->ExpandableStackBottom) + *StackReserve;
318
319   /* expandable stack limit - the lowest committed address of the stack */
320   UserStack->ExpandableStackLimit =
321    (PUCHAR)(UserStack->ExpandableStackBase) - *StackCommit;
322
323   DPRINT("Stack base %p\n", UserStack->ExpandableStackBase);
324   DPRINT("Stack limit %p\n", UserStack->ExpandableStackLimit);
325
326   /* commit as much stack as requested */
327   nErrCode = NtAllocateVirtualMemory
328   (
329    ProcessHandle,
330    &(UserStack->ExpandableStackLimit),
331    0,
332    StackCommit,
333    MEM_COMMIT,
334    PAGE_READWRITE
335   );
336
337   /* failure */
338   if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
339
340   DPRINT("Stack limit %p\n", UserStack->ExpandableStackLimit);
341
342   pGuardBase = (PUCHAR)(UserStack->ExpandableStackLimit) - PAGE_SIZE;
343
344   DPRINT("Guard base %p\n", UserStack->ExpandableStackBase);
345
346   /* set up the guard page */
347   nErrCode = NtAllocateVirtualMemory
348   (
349    ProcessHandle,
350    &pGuardBase,
351    0,
352    &nGuardSize,
353    MEM_COMMIT,
354    PAGE_READWRITE | PAGE_GUARD
355   );
356
357   /* failure */
358   if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
359
360   DPRINT("Guard base %p\n", UserStack->ExpandableStackBase);
361  }
362
363  return STATUS_SUCCESS;
364
365  /* cleanup in case of failure */
366 l_Cleanup:
367   if(UserStack->FixedStackLimit)
368   pStackLowest = UserStack->FixedStackLimit;
369  else if(UserStack->ExpandableStackBottom)
370   pStackLowest = UserStack->ExpandableStackBottom;
371
372  /* free the stack, if it was allocated */
373  if(pStackLowest != NULL)
374   NtFreeVirtualMemory(ProcessHandle, &pStackLowest, &nSize, MEM_RELEASE);
375
376  return nErrCode;
377 }
378
379
380 NTSTATUS
381 LdrLoadInitialProcess(PHANDLE ProcessHandle,
382                       PHANDLE ThreadHandle)
383 {
384   SECTION_IMAGE_INFORMATION Sii;
385   UNICODE_STRING ImagePath;
386   HANDLE SectionHandle;
387   CONTEXT Context;
388   USER_STACK UserStack;
389   ULONG_PTR nStackReserve = 0;
390   ULONG_PTR nStackCommit = 0;
391   PVOID pStackLowest;
392   PVOID pStackBase;
393   ULONG ResultLength;
394   PVOID ImageBaseAddress;
395   ULONG InitialStack[5];
396   NTSTATUS Status;
397
398   /* Get the absolute path to smss.exe. */
399   RtlInitUnicodeStringFromLiteral(&ImagePath,
400                                   L"\\SystemRoot\\system32\\smss.exe");
401
402   /* Map process image */
403   Status = LdrpMapProcessImage(&SectionHandle,
404                                &ImagePath);
405   if (!NT_SUCCESS(Status))
406     {
407       DPRINT("LdrpMapImage() failed (Status %lx)\n", Status);
408       return(Status);
409     }
410
411   /* Get information about the process image. */
412    Status = NtQuerySection(SectionHandle,
413                            SectionImageInformation,
414                            &Sii,
415                            sizeof(Sii),
416                            &ResultLength);
417   if (!NT_SUCCESS(Status) || ResultLength != sizeof(Sii))
418     {
419       DPRINT("ZwQuerySection failed (Status %X)\n", Status);
420       NtClose(ProcessHandle);
421       NtClose(SectionHandle);
422       return(Status);
423     }
424
425   DPRINT("Creating process\n");
426   Status = NtCreateProcess(ProcessHandle,
427                            PROCESS_ALL_ACCESS,
428                            NULL,
429                            SystemProcessHandle,
430                            FALSE,
431                            SectionHandle,
432                            NULL,
433                            NULL);
434   NtClose(SectionHandle);
435   if (!NT_SUCCESS(Status))
436     {
437       DPRINT("NtCreateProcess() failed (Status %lx)\n", Status);
438       return(Status);
439     }
440
441   /* Create process environment */
442   DPRINT("Creating the process environment\n");
443   Status = LdrpCreateProcessEnvironment(*ProcessHandle,
444                                         &ImagePath,
445                                         &ImageBaseAddress);
446   if (!NT_SUCCESS(Status))
447     {
448       DPRINT("LdrpCreateProcessEnvironment() failed (Status %lx)\n", Status);
449       NtClose(*ProcessHandle);
450       return(Status);
451     }
452   DPRINT("ImageBaseAddress: %p\n", ImageBaseAddress);
453
454
455   /* Calculate initial stack sizes */
456   if (Sii.StackReserve > 0x100000)
457     nStackReserve = Sii.StackReserve;
458   else
459     nStackReserve = 0x100000; /* 1MByte */
460
461   /* FIXME */
462 #if 0
463   if (Sii.StackCommit > PAGE_SIZE)
464     nStackCommit =  Sii.StackCommit;
465   else
466     nStackCommit = PAGE_SIZE;
467 #endif
468   nStackCommit = nStackReserve - PAGE_SIZE;
469
470   DPRINT("StackReserve 0x%lX  StackCommit 0x%lX\n",
471          nStackReserve, nStackCommit);
472
473
474   /* Create the process stack */
475   Status = LdrpCreateStack
476   (
477    *ProcessHandle,
478    &UserStack,
479    &nStackReserve,
480    &nStackCommit
481   );
482
483   if (!NT_SUCCESS(Status))
484     {
485       DPRINT("Failed to write initial stack.\n");
486       NtClose(ProcessHandle);
487       return(Status);
488     }
489
490   if(UserStack.FixedStackBase && UserStack.FixedStackLimit)
491   {
492    pStackBase = UserStack.FixedStackBase;
493    pStackLowest = UserStack.FixedStackLimit;
494   }
495   else
496   {
497    pStackBase = UserStack.ExpandableStackBase;
498    pStackLowest = UserStack.ExpandableStackBottom;
499   }
500
501   DPRINT("pStackBase = %p\n", pStackBase);
502   DPRINT("pStackLowest = %p\n", pStackLowest);
503
504   /*
505    * Initialize context to point to LdrStartup
506    */
507 #if defined(_M_IX86)
508   memset(&Context,0,sizeof(CONTEXT));
509   Context.ContextFlags = CONTEXT_FULL;
510   Context.FloatSave.ControlWord = 0xffff037f;
511   Context.FloatSave.StatusWord = 0xffff0000;
512   Context.FloatSave.TagWord = 0xffffffff;
513   Context.FloatSave.DataSelector = 0xffff0000;
514   Context.Eip = (ULONG_PTR)(ImageBaseAddress + (ULONG_PTR)Sii.EntryPoint);
515   Context.SegCs = USER_CS;
516   Context.SegDs = USER_DS;
517   Context.SegEs = USER_DS;
518   Context.SegFs = TEB_SELECTOR;
519   Context.SegGs = USER_DS;
520   Context.SegSs = USER_DS;
521   Context.EFlags = 0x202;
522   Context.Esp = (ULONG_PTR)pStackBase - 20;
523 #else
524 #error Unsupported architecture
525 #endif
526
527   /*
528    * Write in the initial stack.
529    */
530   InitialStack[0] = 0;
531   InitialStack[1] = PEB_BASE;
532   Status = NtWriteVirtualMemory(*ProcessHandle,
533                                 (PVOID)Context.Esp,
534                                 InitialStack,
535                                 sizeof(InitialStack),
536                                 &ResultLength);
537   if (!NT_SUCCESS(Status))
538     {
539       ULONG_PTR nSize = 0;
540
541       DPRINT("Failed to write initial stack.\n");
542
543       NtFreeVirtualMemory(*ProcessHandle,
544                           pStackLowest,
545                           &nSize,
546                           MEM_RELEASE);
547       NtClose(*ProcessHandle);
548       return(Status);
549     }
550
551   /* Create initial thread */
552   DPRINT("Creating thread for initial process\n");
553   Status = NtCreateThread(ThreadHandle,
554                           THREAD_ALL_ACCESS,
555                           NULL,
556                           *ProcessHandle,
557                           NULL,
558                           &Context,
559                           &UserStack,
560                           FALSE);
561   if (!NT_SUCCESS(Status))
562     {
563       ULONG_PTR nSize = 0;
564
565       DPRINT("NtCreateThread() failed (Status %lx)\n", Status);
566
567       NtFreeVirtualMemory(*ProcessHandle,
568                           pStackLowest,
569                           &nSize,
570                           MEM_RELEASE);
571
572       NtClose(*ProcessHandle);
573       return(Status);
574     }
575
576   DPRINT("Process created successfully\n");
577
578   return(STATUS_SUCCESS);
579 }
580
581 /* EOF */