c22d74799d827d950117c449b05332c4a13fa8dc
[reactos.git] / reactos / 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: init.c,v 1.40 2003/04/27 18:58:00 hbirr Exp $
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 */