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