Merge trunk head (r41474)
[reactos.git] / reactos / dll / ntdll / ldr / startup.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: dll/ntdll/ldr/startup.c
5 * PURPOSE: Process startup for PE executables
6 * PROGRAMMERS: Jean Michault
7 * Rex Jolliff (rex@lvcablemodem.com)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntdll.h>
13 #define NDEBUG
14 #include <debug.h>
15 #include <win32k/callback.h>
16
17 VOID RtlInitializeHeapManager(VOID);
18 VOID LdrpInitLoader(VOID);
19 VOID NTAPI RtlpInitDeferedCriticalSection(VOID);
20 NTSTATUS LdrpAttachThread(VOID);
21 VOID RtlpInitializeVectoredExceptionHandling(VOID);
22
23 /* GLOBALS *******************************************************************/
24
25 PLDR_DATA_TABLE_ENTRY ExeModule;
26 static RTL_CRITICAL_SECTION PebLock;
27 static RTL_CRITICAL_SECTION LoaderLock;
28 static RTL_BITMAP TlsBitMap;
29 static RTL_BITMAP TlsExpansionBitMap;
30 static volatile BOOLEAN LdrpInitialized = FALSE;
31 static LONG LdrpInitLock = 0;
32
33 #define VALUE_BUFFER_SIZE 256
34
35 /* FUNCTIONS *****************************************************************/
36
37 BOOLEAN
38 FASTCALL
39 ReadCompatibilitySetting(HANDLE Key,
40 LPWSTR Value,
41 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo,
42 DWORD * Buffer)
43 {
44 UNICODE_STRING ValueName;
45 NTSTATUS Status;
46 ULONG Length;
47
48 RtlInitUnicodeString(&ValueName, Value);
49 Status = NtQueryValueKey(Key,
50 &ValueName,
51 KeyValuePartialInformation,
52 ValueInfo,
53 VALUE_BUFFER_SIZE,
54 &Length);
55
56 if (!NT_SUCCESS(Status) || (ValueInfo->Type != REG_DWORD))
57 {
58 RtlFreeUnicodeString(&ValueName);
59 return FALSE;
60 }
61
62 RtlCopyMemory(Buffer, &ValueInfo->Data[0], sizeof(DWORD));
63 RtlFreeUnicodeString(&ValueName);
64 return TRUE;
65 }
66
67 VOID
68 FASTCALL
69 LoadImageFileExecutionOptions(PPEB Peb)
70 {
71 NTSTATUS Status = STATUS_SUCCESS;
72 ULONG Value = 0;
73 UNICODE_STRING ValueString;
74 UNICODE_STRING ImageName;
75 UNICODE_STRING ImagePathName;
76 WCHAR ValueBuffer[64];
77 ULONG ValueSize;
78
79 if (Peb->ProcessParameters &&
80 Peb->ProcessParameters->ImagePathName.Length > 0)
81 {
82 DPRINT("%wZ\n", &Peb->ProcessParameters->ImagePathName);
83
84 ImagePathName = Peb->ProcessParameters->ImagePathName;
85 ImageName.Buffer = ImagePathName.Buffer + ImagePathName.Length / sizeof(WCHAR);
86 ImageName.Length = 0;
87
88 while (ImagePathName.Buffer < ImageName.Buffer)
89 {
90 ImageName.Buffer--;
91 if (*ImageName.Buffer == L'\\')
92 {
93 ImageName.Buffer++;
94 break;
95 }
96 }
97
98 ImageName.Length = ImagePathName.Length -
99 (ImageName.Buffer - ImagePathName.Buffer) * sizeof(WCHAR);
100 ImageName.MaximumLength = ImageName.Length +
101 ImagePathName.MaximumLength - ImagePathName.Length;
102
103 DPRINT("%wZ\n", &ImageName);
104
105 /* global flag */
106 Status = LdrQueryImageFileExecutionOptions(&ImageName,
107 L"GlobalFlag",
108 REG_SZ,
109 (PVOID)ValueBuffer,
110 sizeof(ValueBuffer),
111 &ValueSize);
112 if (NT_SUCCESS(Status))
113 {
114 ValueString.Buffer = ValueBuffer;
115 ValueString.Length = ValueSize - sizeof(WCHAR);
116 ValueString.MaximumLength = sizeof(ValueBuffer);
117 Status = RtlUnicodeStringToInteger(&ValueString, 16, &Value);
118 if (NT_SUCCESS(Status))
119 {
120 Peb->NtGlobalFlag |= Value;
121 DPRINT("GlobalFlag: Key='%S', Value=0x%lx\n", ValueBuffer, Value);
122 }
123 }
124 /*
125 * FIXME:
126 * read more options
127 */
128 }
129 }
130
131 BOOLEAN
132 FASTCALL
133 LoadCompatibilitySettings(PPEB Peb)
134 {
135 NTSTATUS Status;
136 HANDLE UserKey = NULL;
137 HANDLE KeyHandle;
138 HANDLE SubKeyHandle;
139 OBJECT_ATTRIBUTES ObjectAttributes;
140 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(
141 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers");
142 UNICODE_STRING ValueName;
143 UCHAR ValueBuffer[VALUE_BUFFER_SIZE];
144 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
145 ULONG Length;
146 DWORD MajorVersion, MinorVersion, BuildNumber, PlatformId,
147 SPMajorVersion, SPMinorVersion = 0;
148
149 if (Peb->ProcessParameters &&
150 (Peb->ProcessParameters->ImagePathName.Length > 0))
151 {
152 Status = RtlOpenCurrentUser(KEY_READ, &UserKey);
153 if (!NT_SUCCESS(Status))
154 {
155 return FALSE;
156 }
157
158 InitializeObjectAttributes(&ObjectAttributes,
159 &KeyName,
160 OBJ_CASE_INSENSITIVE,
161 UserKey,
162 NULL);
163
164 Status = NtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
165
166 if (!NT_SUCCESS(Status))
167 {
168 if (UserKey)
169 NtClose(UserKey);
170 return FALSE;
171 }
172
173 /* query version name for application */
174 ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION) ValueBuffer;
175 Status = NtQueryValueKey(KeyHandle,
176 &Peb->ProcessParameters->ImagePathName,
177 KeyValuePartialInformation,
178 ValueBuffer,
179 VALUE_BUFFER_SIZE,
180 &Length);
181
182 if (!NT_SUCCESS(Status) || (ValueInfo->Type != REG_SZ))
183 {
184 NtClose(KeyHandle);
185 if (UserKey)
186 NtClose(UserKey);
187 return FALSE;
188 }
189
190 ValueName.Length = ValueInfo->DataLength;
191 ValueName.MaximumLength = ValueInfo->DataLength;
192 ValueName.Buffer = (PWSTR) ValueInfo->Data;
193
194 /* load version info */
195 InitializeObjectAttributes(&ObjectAttributes,
196 &ValueName,
197 OBJ_CASE_INSENSITIVE,
198 KeyHandle,
199 NULL);
200
201 Status = NtOpenKey(&SubKeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
202
203 if (!NT_SUCCESS(Status))
204 {
205 NtClose(KeyHandle);
206 if (UserKey)
207 NtClose(UserKey);
208 return FALSE;
209 }
210
211 DPRINT("Loading version information for: %wZ\n", &ValueName);
212
213 /* read settings from registry */
214 if (!ReadCompatibilitySetting(SubKeyHandle, L"MajorVersion", ValueInfo, &MajorVersion))
215 goto finish;
216 if (!ReadCompatibilitySetting(SubKeyHandle, L"MinorVersion", ValueInfo, &MinorVersion))
217 goto finish;
218 if (!ReadCompatibilitySetting(SubKeyHandle, L"BuildNumber", ValueInfo, &BuildNumber))
219 goto finish;
220 if (!ReadCompatibilitySetting(SubKeyHandle, L"PlatformId", ValueInfo, &PlatformId))
221 goto finish;
222
223 /* now assign the settings */
224 Peb->OSMajorVersion = (ULONG) MajorVersion;
225 Peb->OSMinorVersion = (ULONG) MinorVersion;
226 Peb->OSBuildNumber = (USHORT) BuildNumber;
227 Peb->OSPlatformId = (ULONG) PlatformId;
228
229 /* optional service pack version numbers */
230 if (ReadCompatibilitySetting(SubKeyHandle,
231 L"SPMajorVersion",
232 ValueInfo,
233 &SPMajorVersion) &&
234 ReadCompatibilitySetting(SubKeyHandle,
235 L"SPMinorVersion",
236 ValueInfo,
237 &SPMinorVersion))
238 {
239 Peb->OSCSDVersion = ((SPMajorVersion & 0xFF) << 8) |
240 (SPMinorVersion & 0xFF);
241 }
242
243 finish:
244 /* we're finished */
245 NtClose(SubKeyHandle);
246 NtClose(KeyHandle);
247 if (UserKey)
248 NtClose(UserKey);
249 return TRUE;
250 }
251
252 return FALSE;
253 }
254
255 static
256 VOID
257 LdrpInit2(PCONTEXT Context,
258 PVOID SystemArgument1,
259 PVOID SystemArgument2)
260 {
261 PIMAGE_NT_HEADERS NTHeaders;
262 PEPFUNC EntryPoint;
263 PIMAGE_DOS_HEADER PEDosHeader;
264 PVOID ImageBase;
265 PPEB Peb = NtCurrentPeb();
266 PLDR_DATA_TABLE_ENTRY NtModule; // ntdll
267 NLSTABLEINFO NlsTable;
268 WCHAR FullNtDllPath[MAX_PATH];
269 SYSTEM_BASIC_INFORMATION SystemInformation;
270 NTSTATUS Status;
271 PVOID BaseAddress = SystemArgument1;
272
273 DPRINT("LdrpInit()\n");
274 DPRINT("Peb %p\n", Peb);
275 ImageBase = Peb->ImageBaseAddress;
276 DPRINT("ImageBase %p\n", ImageBase);
277
278 if (ImageBase <= (PVOID) 0x1000)
279 {
280 DPRINT("ImageBase is null\n");
281 ZwTerminateProcess(NtCurrentProcess(), STATUS_INVALID_IMAGE_FORMAT);
282 }
283
284 /* If MZ header exists */
285 PEDosHeader = (PIMAGE_DOS_HEADER) ImageBase;
286 DPRINT("PEDosHeader %p\n", PEDosHeader);
287
288 if (PEDosHeader->e_magic != IMAGE_DOS_SIGNATURE ||
289 PEDosHeader->e_lfanew == 0L ||
290 *(PULONG)((PUCHAR)ImageBase + PEDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE)
291 {
292 DPRINT1("Image has bad header\n");
293 ZwTerminateProcess(NtCurrentProcess(), STATUS_INVALID_IMAGE_FORMAT);
294 }
295
296 /* normalize process parameters */
297 RtlNormalizeProcessParams(Peb->ProcessParameters);
298
299 /* Initialize NLS data */
300 RtlInitNlsTables(Peb->AnsiCodePageData,
301 Peb->OemCodePageData,
302 Peb->UnicodeCaseTableData,
303 &NlsTable);
304 RtlResetRtlTranslations(&NlsTable);
305
306 NTHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)ImageBase + PEDosHeader->e_lfanew);
307
308 /* Get number of processors */
309 DPRINT("Here\n");
310 Status = ZwQuerySystemInformation(SystemBasicInformation,
311 &SystemInformation,
312 sizeof(SYSTEM_BASIC_INFORMATION),
313 NULL);
314 DPRINT("Here2\n");
315 if (!NT_SUCCESS(Status))
316 {
317 ZwTerminateProcess(NtCurrentProcess(), Status);
318 }
319
320 Peb->NumberOfProcessors = SystemInformation.NumberOfProcessors;
321
322 /* Initialize Critical Section Data */
323 RtlpInitDeferedCriticalSection();
324
325 /* create process heap */
326 RtlInitializeHeapManager();
327 Peb->ProcessHeap = RtlCreateHeap(HEAP_GROWABLE,
328 NULL,
329 NTHeaders->OptionalHeader.SizeOfHeapReserve,
330 NTHeaders->OptionalHeader.SizeOfHeapCommit,
331 NULL,
332 NULL);
333 if (Peb->ProcessHeap == 0)
334 {
335 DPRINT1("Failed to create process heap\n");
336 ZwTerminateProcess(NtCurrentProcess(), STATUS_INSUFFICIENT_RESOURCES);
337 }
338
339 /* initialized vectored exception handling */
340 RtlpInitializeVectoredExceptionHandling();
341
342 /* initalize peb lock support */
343 RtlInitializeCriticalSection(&PebLock);
344 Peb->FastPebLock = &PebLock;
345 Peb->FastPebLockRoutine = (PPEBLOCKROUTINE)RtlEnterCriticalSection;
346 Peb->FastPebUnlockRoutine = (PPEBLOCKROUTINE)RtlLeaveCriticalSection;
347
348 /* initialize tls bitmaps */
349 RtlInitializeBitMap(&TlsBitMap, Peb->TlsBitmapBits, TLS_MINIMUM_AVAILABLE);
350 RtlInitializeBitMap(&TlsExpansionBitMap, Peb->TlsExpansionBitmapBits, TLS_EXPANSION_SLOTS);
351
352 Peb->TlsBitmap = &TlsBitMap;
353 Peb->TlsExpansionBitmap = &TlsExpansionBitMap;
354 Peb->TlsExpansionCounter = TLS_MINIMUM_AVAILABLE;
355
356 /* Initialize table of callbacks for the kernel. */
357 Peb->KernelCallbackTable = RtlAllocateHeap(RtlGetProcessHeap(),
358 0,
359 sizeof(PVOID) *
360 (USER32_CALLBACK_MAXIMUM + 1));
361 if (Peb->KernelCallbackTable == NULL)
362 {
363 DPRINT1("Failed to create callback table\n");
364 ZwTerminateProcess(NtCurrentProcess(), STATUS_INSUFFICIENT_RESOURCES);
365 }
366
367 /* initalize loader lock */
368 RtlInitializeCriticalSection(&LoaderLock);
369 Peb->LoaderLock = &LoaderLock;
370
371 /* create loader information */
372 Peb->Ldr = (PPEB_LDR_DATA) RtlAllocateHeap(Peb->ProcessHeap,
373 0,
374 sizeof(PEB_LDR_DATA));
375 if (Peb->Ldr == NULL)
376 {
377 DPRINT1("Failed to create loader data\n");
378 ZwTerminateProcess(NtCurrentProcess(), STATUS_INSUFFICIENT_RESOURCES);
379 }
380
381 Peb->Ldr->Length = sizeof(PEB_LDR_DATA);
382 Peb->Ldr->Initialized = FALSE;
383 Peb->Ldr->SsHandle = NULL;
384 InitializeListHead(&Peb->Ldr->InLoadOrderModuleList);
385 InitializeListHead(&Peb->Ldr->InMemoryOrderModuleList);
386 InitializeListHead(&Peb->Ldr->InInitializationOrderModuleList);
387
388 /* Load compatibility settings */
389 LoadCompatibilitySettings(Peb);
390
391 /* Load execution options */
392 LoadImageFileExecutionOptions(Peb);
393
394 /* build full ntdll path */
395 wcscpy(FullNtDllPath, SharedUserData->NtSystemRoot);
396 wcscat(FullNtDllPath, L"\\system32\\ntdll.dll");
397
398 /* add entry for ntdll */
399 NtModule = (PLDR_DATA_TABLE_ENTRY)
400 RtlAllocateHeap(Peb->ProcessHeap,
401 0,
402 sizeof(LDR_DATA_TABLE_ENTRY));
403 if (NtModule == NULL)
404 {
405 DPRINT1("Failed to create loader module entry (NTDLL)\n");
406 ZwTerminateProcess(NtCurrentProcess(), STATUS_INSUFFICIENT_RESOURCES);
407 }
408 memset(NtModule, 0, sizeof(LDR_DATA_TABLE_ENTRY));
409
410 NtModule->DllBase = BaseAddress;
411 NtModule->EntryPoint = 0; /* no entry point */
412 RtlCreateUnicodeString(&NtModule->FullDllName, FullNtDllPath);
413 RtlCreateUnicodeString(&NtModule->BaseDllName, L"ntdll.dll");
414 NtModule->Flags = LDRP_IMAGE_DLL | LDRP_ENTRY_PROCESSED;
415
416 NtModule->LoadCount = -1; /* don't unload */
417 NtModule->TlsIndex = -1;
418 NtModule->SectionPointer = NULL;
419 NtModule->CheckSum = 0;
420
421 NTHeaders = RtlImageNtHeader(NtModule->DllBase);
422 NtModule->SizeOfImage = LdrpGetResidentSize(NTHeaders);
423 NtModule->TimeDateStamp = NTHeaders->FileHeader.TimeDateStamp;
424
425 InsertTailList(&Peb->Ldr->InLoadOrderModuleList,
426 &NtModule->InLoadOrderLinks);
427 InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
428 &NtModule->InInitializationOrderLinks);
429
430 #if DBG || defined(KDBG)
431
432 LdrpLoadUserModuleSymbols(NtModule);
433
434 #endif /* DBG || KDBG */
435
436 /* add entry for executable (becomes first list entry) */
437 ExeModule = (PLDR_DATA_TABLE_ENTRY)
438 RtlAllocateHeap(Peb->ProcessHeap,
439 0,
440 sizeof(LDR_DATA_TABLE_ENTRY));
441 if (ExeModule == NULL)
442 {
443 DPRINT1("Failed to create loader module infomation\n");
444 ZwTerminateProcess(NtCurrentProcess(), STATUS_INSUFFICIENT_RESOURCES);
445 }
446
447 ExeModule->DllBase = Peb->ImageBaseAddress;
448
449 if ((Peb->ProcessParameters == NULL) ||
450 (Peb->ProcessParameters->ImagePathName.Length == 0))
451 {
452 DPRINT1("Failed to access the process parameter block\n");
453 ZwTerminateProcess(NtCurrentProcess(), STATUS_UNSUCCESSFUL);
454 }
455
456 RtlCreateUnicodeString(&ExeModule->FullDllName,
457 Peb->ProcessParameters->ImagePathName.Buffer);
458 RtlCreateUnicodeString(&ExeModule->BaseDllName,
459 wcsrchr(ExeModule->FullDllName.Buffer, L'\\') + 1);
460
461 DPRINT("BaseDllName '%wZ' FullDllName '%wZ'\n", &ExeModule->BaseDllName, &ExeModule->FullDllName);
462
463 ExeModule->Flags = LDRP_ENTRY_PROCESSED;
464 ExeModule->LoadCount = -1; /* don't unload */
465 ExeModule->TlsIndex = -1;
466 ExeModule->SectionPointer = NULL;
467 ExeModule->CheckSum = 0;
468
469 NTHeaders = RtlImageNtHeader(ExeModule->DllBase);
470 ExeModule->SizeOfImage = LdrpGetResidentSize(NTHeaders);
471 ExeModule->TimeDateStamp = NTHeaders->FileHeader.TimeDateStamp;
472
473 InsertHeadList(&Peb->Ldr->InLoadOrderModuleList,
474 &ExeModule->InLoadOrderLinks);
475
476 LdrpInitLoader();
477
478 #if DBG || defined(KDBG)
479
480 LdrpLoadUserModuleSymbols(ExeModule);
481
482 #endif /* DBG || KDBG */
483
484 EntryPoint = LdrPEStartup((PVOID)ImageBase, NULL, NULL, NULL);
485 ExeModule->EntryPoint = EntryPoint;
486
487 /* all required dlls are loaded now */
488 Peb->Ldr->Initialized = TRUE;
489
490 /* Check before returning that we can run the image safely. */
491 if (EntryPoint == NULL)
492 {
493 DPRINT1("Failed to initialize image\n");
494 ZwTerminateProcess(NtCurrentProcess(), STATUS_INVALID_IMAGE_FORMAT);
495 }
496
497 /* Break into debugger */
498 if (Peb->BeingDebugged)
499 DbgBreakPoint();
500 }
501
502 VOID
503 NTAPI
504 LdrpInit(PCONTEXT Context,
505 PVOID SystemArgument1,
506 PVOID SystemArgument2)
507 {
508 if (!LdrpInitialized)
509 {
510 if (!_InterlockedExchange(&LdrpInitLock, 1))
511 {
512 LdrpInit2(Context, SystemArgument1, SystemArgument2);
513 LdrpInitialized = TRUE;
514 }
515 else
516 {
517 LARGE_INTEGER Interval = {{-200000, -1}};
518
519 do
520 {
521 NtDelayExecution(FALSE, &Interval);
522 }
523 while (!LdrpInitialized);
524 }
525 }
526
527 /* attach the thread */
528 RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
529 LdrpAttachThread();
530 RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
531 }
532
533 /* EOF */