9b504e9d535010da43aba8f6853461e5bb41859b
[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 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/ldr/init.c
22 * PURPOSE: Loaders for PE executables
23 * PROGRAMMERS: Jean Michault
24 * Rex Jolliff (rex@lvcablemodem.com)
25 * UPDATE HISTORY:
26 * DW 22/05/98 Created
27 * RJJ 10/12/98 Completed image loader function and added hooks for MZ/PE
28 * RJJ 10/12/98 Built driver loader function and added hooks for PE/COFF
29 * RJJ 10/12/98 Rolled in David's code to load COFF drivers
30 * JM 14/12/98 Built initial PE user module loader
31 * RJJ 06/03/99 Moved user PE loader into NTDLL
32 * EA 19990717 LdrGetSystemDirectory()
33 * EK 20000618 Using SystemRoot link instead of LdrGetSystemDirectory()
34 */
35
36 /* INCLUDES *****************************************************************/
37
38 #include <ddk/ntddk.h>
39 #include <internal/i386/segment.h>
40 #include <internal/module.h>
41 #include <internal/ntoskrnl.h>
42 #include <internal/ob.h>
43 #include <internal/ps.h>
44 #include <internal/ldr.h>
45 #include <napi/teb.h>
46
47 #define NDEBUG
48 #include <internal/debug.h>
49
50 /* FUNCTIONS *****************************************************************/
51
52 /*
53 * FIXME: The location of the initial process should be configurable,
54 * from command line or registry
55 */
56 NTSTATUS LdrLoadInitialProcess (VOID)
57 {
58 NTSTATUS Status;
59 HANDLE ProcessHandle;
60 UNICODE_STRING ProcessName;
61 OBJECT_ATTRIBUTES ObjectAttributes;
62 HANDLE FileHandle;
63 HANDLE SectionHandle;
64 PIMAGE_NT_HEADERS NTHeaders;
65 PEPROCESS Process;
66 CONTEXT Context;
67 HANDLE ThreadHandle;
68 INITIAL_TEB InitialTeb;
69 ULONG OldPageProtection;
70 SECTION_IMAGE_INFORMATION Sii;
71 ULONG ResultLength;
72 PVOID ImageBaseAddress;
73 ULONG InitialStack[5];
74
75 /*
76 * Get the absolute path to smss.exe using the
77 * SystemRoot link.
78 */
79 RtlInitUnicodeString(&ProcessName,
80 L"\\SystemRoot\\system32\\smss.exe");
81
82 /*
83 * Open process image to determine ImageBase
84 * and StackBase/Size.
85 */
86 InitializeObjectAttributes(&ObjectAttributes,
87 &ProcessName,
88 0,
89 NULL,
90 NULL);
91 DPRINT("Opening image file %S\n", ObjectAttributes.ObjectName->Buffer);
92 Status = ZwOpenFile(&FileHandle,
93 FILE_ALL_ACCESS,
94 &ObjectAttributes,
95 NULL,
96 0,
97 0);
98 if (!NT_SUCCESS(Status))
99 {
100 DPRINT("Image open failed (Status was %x)\n", Status);
101 return Status;
102 }
103
104 /*
105 * Create a section for the image
106 */
107 DPRINT("Creating section\n");
108 Status = ZwCreateSection(&SectionHandle,
109 SECTION_ALL_ACCESS,
110 NULL,
111 NULL,
112 PAGE_READWRITE,
113 SEC_COMMIT | SEC_IMAGE,
114 FileHandle);
115 if (!NT_SUCCESS(Status))
116 {
117 DPRINT("ZwCreateSection failed (Status %x)\n", Status);
118 ZwClose(FileHandle);
119 return(Status);
120 }
121 ZwClose(FileHandle);
122
123 /*
124 * Get information about the process image.
125 */
126 Status = ZwQuerySection(SectionHandle,
127 SectionImageInformation,
128 &Sii,
129 sizeof(Sii),
130 &ResultLength);
131 if (!NT_SUCCESS(Status) || ResultLength != sizeof(Sii))
132 {
133 DPRINT("ZwQuerySection failed (Status %X)\n", Status);
134 ZwClose(SectionHandle);
135 return(Status);
136 }
137
138 DPRINT("Creating process\n");
139 Status = ZwCreateProcess(&ProcessHandle,
140 PROCESS_ALL_ACCESS,
141 NULL,
142 SystemProcessHandle,
143 FALSE,
144 SectionHandle,
145 NULL,
146 NULL);
147 if (!NT_SUCCESS(Status))
148 {
149 DPRINT("Could not create process\n");
150 return Status;
151 }
152
153 /*
154 * Create initial stack and thread
155 */
156
157 /*
158 * Create page backed section for stack
159 */
160 DPRINT("Allocating stack\n");
161
162 DPRINT("Referencing process\n");
163 Status = ObReferenceObjectByHandle(ProcessHandle,
164 PROCESS_ALL_ACCESS,
165 PsProcessType,
166 KernelMode,
167 (PVOID*)&Process,
168 NULL);
169 if (!NT_SUCCESS(Status))
170 {
171 DPRINT("ObReferenceObjectByProcess() failed (Status %x)\n", Status);
172 return(Status);
173 }
174
175 DPRINT("Attaching to process\n");
176 KeAttachProcess(Process);
177 ImageBaseAddress = Process->Peb->ImageBaseAddress;
178 NTHeaders = RtlImageNtHeader(ImageBaseAddress);
179 DPRINT("NTHeaders %x\n", NTHeaders);
180 InitialTeb.StackReserve = NTHeaders->OptionalHeader.SizeOfStackReserve;
181 /* FIXME: use correct commit size */
182 InitialTeb.StackCommit = NTHeaders->OptionalHeader.SizeOfStackReserve - PAGESIZE;
183 // InitialTeb.StackCommit = NTHeaders->OptionalHeader.SizeOfStackCommit;
184 /* add guard page size */
185 InitialTeb.StackCommit += PAGESIZE;
186 DPRINT("StackReserve 0x%lX StackCommit 0x%lX\n",
187 InitialTeb.StackReserve, InitialTeb.StackCommit);
188 KeDetachProcess();
189 DPRINT("Dereferencing process\n");
190 ObDereferenceObject(Process);
191
192 DPRINT("Allocating stack\n");
193 InitialTeb.StackAllocate = NULL;
194 Status = NtAllocateVirtualMemory(ProcessHandle,
195 &InitialTeb.StackAllocate,
196 0,
197 &InitialTeb.StackReserve,
198 MEM_RESERVE,
199 PAGE_READWRITE);
200 if (!NT_SUCCESS(Status))
201 {
202 DPRINT("Stack allocation failed (Status %x)", Status);
203 return(Status);
204 }
205
206 DPRINT("StackAllocate: %p ReserveSize: 0x%lX\n",
207 InitialTeb.StackAllocate, InitialTeb.StackReserve);
208
209 InitialTeb.StackBase = (PVOID)((ULONG)InitialTeb.StackAllocate + InitialTeb.StackReserve);
210 InitialTeb.StackLimit = (PVOID)((ULONG)InitialTeb.StackBase - InitialTeb.StackCommit);
211
212 DPRINT("StackBase: %p StackCommit: 0x%lX\n",
213 InitialTeb.StackBase, InitialTeb.StackCommit);
214
215 /* Commit stack */
216 Status = NtAllocateVirtualMemory(ProcessHandle,
217 &InitialTeb.StackLimit,
218 0,
219 &InitialTeb.StackCommit,
220 MEM_COMMIT,
221 PAGE_READWRITE);
222 if (!NT_SUCCESS(Status))
223 {
224 /* release the stack space */
225 NtFreeVirtualMemory(ProcessHandle,
226 InitialTeb.StackAllocate,
227 &InitialTeb.StackReserve,
228 MEM_RELEASE);
229
230 DPRINT("Error comitting stack page!\n");
231 return(Status);
232 }
233
234 DPRINT("StackLimit: %p\nStackCommit: 0x%lX\n",
235 InitialTeb.StackLimit,
236 InitialTeb.StackCommit);
237
238 /* Protect guard page */
239 Status = NtProtectVirtualMemory(ProcessHandle,
240 InitialTeb.StackLimit,
241 PAGESIZE,
242 PAGE_GUARD | PAGE_READWRITE,
243 &OldPageProtection);
244 if (!NT_SUCCESS(Status))
245 {
246 /* release the stack space */
247 NtFreeVirtualMemory(ProcessHandle,
248 InitialTeb.StackAllocate,
249 &InitialTeb.StackReserve,
250 MEM_RELEASE);
251
252 DPRINT("Error protecting guard page!\n");
253 return(Status);
254 }
255
256 /*
257 * Initialize context to point to LdrStartup
258 */
259 memset(&Context,0,sizeof(CONTEXT));
260 Context.Eip = (ULONG)(ImageBaseAddress + (ULONG)Sii.EntryPoint);
261 Context.SegCs = USER_CS;
262 Context.SegDs = USER_DS;
263 Context.SegEs = USER_DS;
264 Context.SegFs = TEB_SELECTOR;
265 Context.SegGs = USER_DS;
266 Context.SegSs = USER_DS;
267 Context.EFlags = 0x202;
268 Context.Esp = (ULONG)InitialTeb.StackBase - 20;
269
270 /*
271 * Write in the initial stack.
272 */
273 InitialStack[0] = 0;
274 InitialStack[1] = PEB_BASE;
275 Status = ZwWriteVirtualMemory(ProcessHandle,
276 (PVOID)Context.Esp,
277 InitialStack,
278 sizeof(InitialStack),
279 &ResultLength);
280 if (!NT_SUCCESS(Status))
281 {
282 DPRINT1("Failed to write initial stack.\n");
283 return(Status);
284 }
285
286 /*
287 * FIXME: Create process and let 'er rip
288 */
289 DPRINT("Creating thread for initial process\n");
290 Status = ZwCreateThread(&ThreadHandle,
291 THREAD_ALL_ACCESS,
292 NULL,
293 ProcessHandle,
294 NULL,
295 &Context,
296 &InitialTeb,
297 FALSE);
298 if (!NT_SUCCESS(Status))
299 {
300 DPRINT("Thread creation failed (Status %x)\n", Status);
301
302 NtFreeVirtualMemory(ProcessHandle,
303 InitialTeb.StackAllocate,
304 &InitialTeb.StackReserve,
305 MEM_RELEASE);
306
307 /* FIXME: unmap the section here */
308 /* FIXME: destroy the section here */
309 /* FIXME: Kill the process here */
310
311 return(Status);
312 }
313
314 return(STATUS_SUCCESS);
315 }
316
317 /* EOF */