3 * Copyright (C) 1998, 1999, 2000, 2001, 2002 ReactOS Team
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.
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.
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.
19 /* $Id: init.c,v 1.40 2003/04/27 18:58:00 hbirr Exp $
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)
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.
38 /* INCLUDES *****************************************************************/
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>
50 #include <internal/debug.h>
53 /* MACROS ******************************************************************/
55 #define DENORMALIZE(x,addr) {if(x) x=(VOID*)((ULONG)(x)-(ULONG)(addr));}
56 #define ALIGN(x,align) (((ULONG)(x)+(align)-1UL)&(~((align)-1UL)))
59 /* FUNCTIONS *****************************************************************/
62 LdrpMapProcessImage(PHANDLE SectionHandle
,
63 PUNICODE_STRING ImagePath
)
65 OBJECT_ATTRIBUTES ObjectAttributes
;
70 InitializeObjectAttributes(&ObjectAttributes
,
76 DPRINT("Opening image file %S\n", ObjectAttributes
.ObjectName
->Buffer
);
77 Status
= NtOpenFile(&FileHandle
,
83 if (!NT_SUCCESS(Status
))
85 DPRINT("NtOpenFile() failed (Status %lx)\n", Status
);
89 /* Create a section for the image */
90 DPRINT("Creating section\n");
91 Status
= NtCreateSection(SectionHandle
,
96 SEC_COMMIT
| SEC_IMAGE
,
99 if (!NT_SUCCESS(Status
))
101 DPRINT("NtCreateSection() failed (Status %lx)\n", Status
);
109 LdrpCreateProcessEnvironment(HANDLE ProcessHandle
,
110 PUNICODE_STRING ImagePath
,
111 PVOID
* ImageBaseAddress
)
113 PRTL_USER_PROCESS_PARAMETERS LocalPpb
;
114 PRTL_USER_PROCESS_PARAMETERS ProcessPpb
;
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
);
127 /* Allocate the local PPB */
129 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
133 MEM_RESERVE
| MEM_COMMIT
,
135 if (!NT_SUCCESS(Status
))
137 DPRINT("NtAllocateVirtualMemory() failed (Status %lx)\n", Status
);
141 DPRINT("LocalPpb %p AllocationSize %lu\n", LocalPpb
, RegionSize
);
143 /* Initialize the local PPB */
144 RtlZeroMemory(LocalPpb
,
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);
152 /* Copy image path */
153 RtlCopyMemory(LocalPpb
->ImagePathName
.Buffer
,
156 LocalPpb
->ImagePathName
.Buffer
[ImagePath
->Length
/ sizeof(WCHAR
)] = (WCHAR
)0;
158 /* Denormalize the process parameter block */
159 DENORMALIZE(LocalPpb
->ImagePathName
.Buffer
, LocalPpb
);
160 LocalPpb
->Flags
&= ~PPF_NORMALIZED
;
162 /* Create the process PPB */
164 Status
= NtAllocateVirtualMemory(ProcessHandle
,
168 MEM_RESERVE
| MEM_COMMIT
,
170 if (!NT_SUCCESS(Status
))
172 DPRINT("NtAllocateVirtualMemory() failed (Status %lx)\n", Status
);
174 /* Release the local PPB */
176 NtFreeVirtualMemory(NtCurrentProcess(),
183 /* Copy local PPB into the process PPB */
184 NtWriteVirtualMemory(ProcessHandle
,
187 LocalPpb
->AllocationSize
,
190 /* Update pointer to process PPB in the process PEB */
191 Offset
= FIELD_OFFSET(PEB
, ProcessParameters
);
192 NtWriteVirtualMemory(ProcessHandle
,
193 (PVOID
)(PEB_BASE
+ Offset
),
198 /* Release local PPB */
200 NtFreeVirtualMemory(NtCurrentProcess(),
205 /* Set image file name */
206 Status
= NtSetInformationProcess(ProcessHandle
,
207 ProcessImageFileName
,
210 if (!NT_SUCCESS(Status
))
212 DPRINT("NtSetInformationProcess() failed (Status %lx)\n", Status
);
216 /* Read image base address. */
217 Offset
= FIELD_OFFSET(PEB
, ImageBaseAddress
);
218 NtReadVirtualMemory(ProcessHandle
,
219 (PVOID
)(PEB_BASE
+ Offset
),
224 return(STATUS_SUCCESS
);
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]
237 static NTSTATUS LdrpCreateStack
239 HANDLE ProcessHandle
,
240 PUSER_STACK UserStack
,
241 PULONG_PTR StackReserve
,
242 PULONG_PTR StackCommit
245 PVOID pStackLowest
= NULL
;
249 if(StackReserve
== NULL
|| StackCommit
== NULL
)
250 return STATUS_INVALID_PARAMETER
;
252 /* FIXME: no SEH, no guard pages */
253 *StackCommit
= *StackReserve
;
255 UserStack
->FixedStackBase
= NULL
;
256 UserStack
->FixedStackLimit
= NULL
;
257 UserStack
->ExpandableStackBase
= NULL
;
258 UserStack
->ExpandableStackLimit
= NULL
;
259 UserStack
->ExpandableStackBottom
= NULL
;
261 /* FIXME: this code assumes a stack growing downwards */
263 if(*StackCommit
== *StackReserve
)
265 DPRINT("Fixed stack\n");
267 UserStack
->FixedStackLimit
= NULL
;
269 /* allocate the stack */
270 nErrCode
= NtAllocateVirtualMemory
273 &(UserStack
->FixedStackLimit
),
276 MEM_RESERVE
| MEM_COMMIT
,
281 if(!NT_SUCCESS(nErrCode
)) return nErrCode
;
283 /* store the highest (first) address of the stack */
284 UserStack
->FixedStackBase
=
285 (PUCHAR
)(UserStack
->FixedStackLimit
) + *StackReserve
;
287 /* expandable stack */
290 ULONG_PTR nGuardSize
= PAGE_SIZE
;
293 DPRINT("Expandable stack\n");
295 UserStack
->FixedStackLimit
= NULL
;
296 UserStack
->FixedStackBase
= NULL
;
297 UserStack
->ExpandableStackBottom
= NULL
;
299 /* reserve the stack */
300 nErrCode
= NtAllocateVirtualMemory
303 &(UserStack
->ExpandableStackBottom
),
311 if(!NT_SUCCESS(nErrCode
)) return nErrCode
;
313 DPRINT("Reserved %08X bytes\n", *StackReserve
);
315 /* expandable stack base - the highest address of the stack */
316 UserStack
->ExpandableStackBase
=
317 (PUCHAR
)(UserStack
->ExpandableStackBottom
) + *StackReserve
;
319 /* expandable stack limit - the lowest committed address of the stack */
320 UserStack
->ExpandableStackLimit
=
321 (PUCHAR
)(UserStack
->ExpandableStackBase
) - *StackCommit
;
323 DPRINT("Stack base %p\n", UserStack
->ExpandableStackBase
);
324 DPRINT("Stack limit %p\n", UserStack
->ExpandableStackLimit
);
326 /* commit as much stack as requested */
327 nErrCode
= NtAllocateVirtualMemory
330 &(UserStack
->ExpandableStackLimit
),
338 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
340 DPRINT("Stack limit %p\n", UserStack
->ExpandableStackLimit
);
342 pGuardBase
= (PUCHAR
)(UserStack
->ExpandableStackLimit
) - PAGE_SIZE
;
344 DPRINT("Guard base %p\n", UserStack
->ExpandableStackBase
);
346 /* set up the guard page */
347 nErrCode
= NtAllocateVirtualMemory
354 PAGE_READWRITE
| PAGE_GUARD
358 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
360 DPRINT("Guard base %p\n", UserStack
->ExpandableStackBase
);
363 return STATUS_SUCCESS
;
365 /* cleanup in case of failure */
367 if(UserStack
->FixedStackLimit
)
368 pStackLowest
= UserStack
->FixedStackLimit
;
369 else if(UserStack
->ExpandableStackBottom
)
370 pStackLowest
= UserStack
->ExpandableStackBottom
;
372 /* free the stack, if it was allocated */
373 if(pStackLowest
!= NULL
)
374 NtFreeVirtualMemory(ProcessHandle
, &pStackLowest
, &nSize
, MEM_RELEASE
);
381 LdrLoadInitialProcess(PHANDLE ProcessHandle
,
382 PHANDLE ThreadHandle
)
384 SECTION_IMAGE_INFORMATION Sii
;
385 UNICODE_STRING ImagePath
;
386 HANDLE SectionHandle
;
388 USER_STACK UserStack
;
389 ULONG_PTR nStackReserve
= 0;
390 ULONG_PTR nStackCommit
= 0;
394 PVOID ImageBaseAddress
;
395 ULONG InitialStack
[5];
398 /* Get the absolute path to smss.exe. */
399 RtlInitUnicodeStringFromLiteral(&ImagePath
,
400 L
"\\SystemRoot\\system32\\smss.exe");
402 /* Map process image */
403 Status
= LdrpMapProcessImage(&SectionHandle
,
405 if (!NT_SUCCESS(Status
))
407 DPRINT("LdrpMapImage() failed (Status %lx)\n", Status
);
411 /* Get information about the process image. */
412 Status
= NtQuerySection(SectionHandle
,
413 SectionImageInformation
,
417 if (!NT_SUCCESS(Status
) || ResultLength
!= sizeof(Sii
))
419 DPRINT("ZwQuerySection failed (Status %X)\n", Status
);
420 NtClose(ProcessHandle
);
421 NtClose(SectionHandle
);
425 DPRINT("Creating process\n");
426 Status
= NtCreateProcess(ProcessHandle
,
434 NtClose(SectionHandle
);
435 if (!NT_SUCCESS(Status
))
437 DPRINT("NtCreateProcess() failed (Status %lx)\n", Status
);
441 /* Create process environment */
442 DPRINT("Creating the process environment\n");
443 Status
= LdrpCreateProcessEnvironment(*ProcessHandle
,
446 if (!NT_SUCCESS(Status
))
448 DPRINT("LdrpCreateProcessEnvironment() failed (Status %lx)\n", Status
);
449 NtClose(*ProcessHandle
);
452 DPRINT("ImageBaseAddress: %p\n", ImageBaseAddress
);
455 /* Calculate initial stack sizes */
456 if (Sii
.StackReserve
> 0x100000)
457 nStackReserve
= Sii
.StackReserve
;
459 nStackReserve
= 0x100000; /* 1MByte */
463 if (Sii
.StackCommit
> PAGE_SIZE
)
464 nStackCommit
= Sii
.StackCommit
;
466 nStackCommit
= PAGE_SIZE
;
468 nStackCommit
= nStackReserve
- PAGE_SIZE
;
470 DPRINT("StackReserve 0x%lX StackCommit 0x%lX\n",
471 nStackReserve
, nStackCommit
);
474 /* Create the process stack */
475 Status
= LdrpCreateStack
483 if (!NT_SUCCESS(Status
))
485 DPRINT("Failed to write initial stack.\n");
486 NtClose(ProcessHandle
);
490 if(UserStack
.FixedStackBase
&& UserStack
.FixedStackLimit
)
492 pStackBase
= UserStack
.FixedStackBase
;
493 pStackLowest
= UserStack
.FixedStackLimit
;
497 pStackBase
= UserStack
.ExpandableStackBase
;
498 pStackLowest
= UserStack
.ExpandableStackBottom
;
501 DPRINT("pStackBase = %p\n", pStackBase
);
502 DPRINT("pStackLowest = %p\n", pStackLowest
);
505 * Initialize context to point to LdrStartup
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;
524 #error Unsupported architecture
528 * Write in the initial stack.
531 InitialStack
[1] = PEB_BASE
;
532 Status
= NtWriteVirtualMemory(*ProcessHandle
,
535 sizeof(InitialStack
),
537 if (!NT_SUCCESS(Status
))
541 DPRINT("Failed to write initial stack.\n");
543 NtFreeVirtualMemory(*ProcessHandle
,
547 NtClose(*ProcessHandle
);
551 /* Create initial thread */
552 DPRINT("Creating thread for initial process\n");
553 Status
= NtCreateThread(ThreadHandle
,
561 if (!NT_SUCCESS(Status
))
565 DPRINT("NtCreateThread() failed (Status %lx)\n", Status
);
567 NtFreeVirtualMemory(*ProcessHandle
,
572 NtClose(*ProcessHandle
);
576 DPRINT("Process created successfully\n");
578 return(STATUS_SUCCESS
);