mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2032 lines
64 KiB
2032 lines
64 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ldrinit.c
|
|
|
|
Abstract:
|
|
|
|
This module implements loader initialization.
|
|
|
|
Author:
|
|
|
|
Mike O'Leary (mikeol) 26-Mar-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <ntos.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <heap.h>
|
|
#include "ldrp.h"
|
|
#include <ctype.h>
|
|
|
|
BOOLEAN LdrpShutdownInProgress = FALSE;
|
|
BOOLEAN LdrpImageHasTls = FALSE;
|
|
BOOLEAN LdrpVerifyDlls = FALSE;
|
|
BOOLEAN LdrpLdrDatabaseIsSetup = FALSE;
|
|
BOOLEAN LdrpInLdrInit = FALSE;
|
|
|
|
PVOID NtDllBase;
|
|
|
|
#if defined(MIPS) || defined(_ALPHA_)
|
|
ULONG LdrpGpValue;
|
|
#endif // MIPS || ALPHA
|
|
|
|
#if DBG
|
|
|
|
ULONG LdrpEventIdBuffer[ 512 ];
|
|
ULONG LdrpEventIdBufferSize;
|
|
|
|
PRTL_EVENT_ID_INFO RtlpCreateHeapEventId;
|
|
PRTL_EVENT_ID_INFO RtlpDestroyHeapEventId;
|
|
PRTL_EVENT_ID_INFO RtlpAllocHeapEventId;
|
|
PRTL_EVENT_ID_INFO RtlpReAllocHeapEventId;
|
|
PRTL_EVENT_ID_INFO RtlpFreeHeapEventId;
|
|
PRTL_EVENT_ID_INFO LdrpCreateProcessEventId;
|
|
PRTL_EVENT_ID_INFO LdrpLoadModuleEventId;
|
|
PRTL_EVENT_ID_INFO LdrpUnloadModuleEventId;
|
|
|
|
#endif // DBG
|
|
|
|
#if defined (_X86_)
|
|
void
|
|
LdrpValidateImageForMp(
|
|
IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry
|
|
);
|
|
#endif
|
|
|
|
NTSTATUS
|
|
LdrpForkProcess( VOID );
|
|
|
|
VOID
|
|
LdrpInitializeThread(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
NtdllOkayToLockRoutine(
|
|
IN PVOID Lock
|
|
);
|
|
|
|
VOID
|
|
RtlpInitDeferedCriticalSection( VOID );
|
|
|
|
VOID
|
|
RtlpCurdirInit();
|
|
|
|
PVOID
|
|
NtdllpAllocateStringRoutine(
|
|
ULONG NumberOfBytes
|
|
)
|
|
{
|
|
return RtlAllocateHeap(RtlProcessHeap(), 0, NumberOfBytes);
|
|
}
|
|
|
|
VOID
|
|
NtdllpFreeStringRoutine(
|
|
PVOID Buffer
|
|
)
|
|
{
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Buffer);
|
|
}
|
|
|
|
PRTL_ALLOCATE_STRING_ROUTINE RtlAllocateStringRoutine;
|
|
PRTL_FREE_STRING_ROUTINE RtlFreeStringRoutine;
|
|
RTL_BITMAP TlsBitMap;
|
|
|
|
RTL_CRITICAL_SECTION_DEBUG LoaderLockDebug;
|
|
RTL_CRITICAL_SECTION LoaderLock = {
|
|
&LoaderLockDebug,
|
|
-1
|
|
};
|
|
BOOLEAN LoaderLockInitialized;
|
|
|
|
|
|
#if defined(MIPS) || defined(_ALPHA_)
|
|
VOID
|
|
LdrpSetGp(
|
|
IN ULONG GpValue
|
|
);
|
|
#endif // MIPS || ALPHA
|
|
|
|
VOID
|
|
LdrpInitializationFailure(
|
|
IN NTSTATUS FailureCode
|
|
)
|
|
{
|
|
|
|
NTSTATUS ErrorStatus;
|
|
ULONG ErrorParameter;
|
|
ULONG ErrorResponse;
|
|
|
|
if ( LdrpFatalHardErrorCount ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Its error time...
|
|
//
|
|
ErrorParameter = (ULONG)FailureCode;
|
|
ErrorStatus = NtRaiseHardError(
|
|
STATUS_APP_INIT_FAILURE,
|
|
1,
|
|
0,
|
|
&ErrorParameter,
|
|
OptionOk,
|
|
&ErrorResponse
|
|
);
|
|
}
|
|
|
|
|
|
VOID
|
|
LdrpInitialize (
|
|
IN PCONTEXT Context,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called as a User-Mode APC routine as the first
|
|
user-mode code executed by a new thread. It's function is to initialize
|
|
loader context, perform module initialization callouts...
|
|
|
|
Arguments:
|
|
|
|
Context - Supplies an optional context buffer that will be restore
|
|
after all DLL initialization has been completed. If this
|
|
parameter is NULL then this is a dynamic snap of this module.
|
|
Otherwise this is a static snap prior to the user process
|
|
gaining control.
|
|
|
|
SystemArgument1 - Supplies the base address of the System Dll.
|
|
|
|
SystemArgument2 - not used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS st, InitStatus;
|
|
PPEB Peb;
|
|
PTEB Teb;
|
|
UNICODE_STRING UnicodeImageName;
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
BOOLEAN AlreadyFailed;
|
|
LARGE_INTEGER DelayValue;
|
|
|
|
SystemArgument2;
|
|
|
|
AlreadyFailed = FALSE;
|
|
Peb = NtCurrentPeb();
|
|
Teb = NtCurrentTeb();
|
|
|
|
if (!Peb->Ldr) {
|
|
#if defined(MIPS) || defined(_ALPHA_)
|
|
ULONG temp;
|
|
#if defined(MIPS)
|
|
Peb->ProcessStarterHelper = (PVOID)LdrProcessStarterHelper;
|
|
#endif
|
|
//
|
|
// Set GP register
|
|
//
|
|
LdrpGpValue =(ULONG)RtlImageDirectoryEntryToData(
|
|
Peb->ImageBaseAddress,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
|
|
&temp
|
|
);
|
|
if (Context != NULL) {
|
|
LdrpSetGp( LdrpGpValue );
|
|
#if defined(_MIPS_)
|
|
Context->XIntGp = (LONG)LdrpGpValue;
|
|
#else
|
|
Context->IntGp = LdrpGpValue;
|
|
#endif
|
|
}
|
|
#endif // MIPS || ALPHA
|
|
|
|
NtGlobalFlag = Peb->NtGlobalFlag;
|
|
#if DBG
|
|
if (TRUE)
|
|
#else
|
|
if (Peb->BeingDebugged || Peb->ReadImageFileExecOptions)
|
|
#endif
|
|
{
|
|
PWSTR pw;
|
|
|
|
pw = (PWSTR)Peb->ProcessParameters->ImagePathName.Buffer;
|
|
if (!(Peb->ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
|
|
pw = (PWSTR)((PCHAR)pw + (ULONG)(Peb->ProcessParameters));
|
|
}
|
|
UnicodeImageName.Buffer = pw;
|
|
UnicodeImageName.Length = Peb->ProcessParameters->ImagePathName.Length;
|
|
UnicodeImageName.MaximumLength = UnicodeImageName.Length;
|
|
|
|
st = LdrQueryImageFileExecutionOptions( &UnicodeImageName,
|
|
L"GlobalFlag",
|
|
REG_DWORD,
|
|
&NtGlobalFlag,
|
|
sizeof( NtGlobalFlag ),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS( st )) {
|
|
UnicodeImageName.Length = 0;
|
|
|
|
if (Peb->BeingDebugged) {
|
|
NtGlobalFlag |= FLG_HEAP_ENABLE_FREE_CHECK |
|
|
FLG_HEAP_ENABLE_TAIL_CHECK |
|
|
FLG_HEAP_VALIDATE_PARAMETERS;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DBG && FLG_HEAP_PAGE_ALLOCS
|
|
|
|
if ( NtGlobalFlag & FLG_HEAP_PAGE_ALLOCS ) {
|
|
|
|
//
|
|
// Turn on BOOLEAN RtlpDebugPageHeap to indicate that
|
|
// new heaps should be created with debug page heap manager
|
|
// when possible. Also force off other heap debug flags
|
|
// that can cause conflicts with the debug page heap
|
|
// manager.
|
|
//
|
|
|
|
RtlpDebugPageHeap = TRUE;
|
|
|
|
NtGlobalFlag &= ~( FLG_HEAP_ENABLE_TAGGING |
|
|
FLG_HEAP_ENABLE_TAG_BY_DLL
|
|
);
|
|
|
|
}
|
|
|
|
#endif // DBG && FLG_HEAP_PAGE_ALLOCS
|
|
|
|
}
|
|
#if defined(MIPS) || defined(_ALPHA_)
|
|
else
|
|
if (Context != NULL) {
|
|
LdrpSetGp( LdrpGpValue );
|
|
#if defined(_MIPS_)
|
|
Context->XIntGp = (LONG)LdrpGpValue;
|
|
#else
|
|
Context->IntGp = LdrpGpValue;
|
|
#endif
|
|
}
|
|
#endif // MIPS || ALPHA
|
|
|
|
ShowSnaps = (BOOLEAN)(FLG_SHOW_LDR_SNAPS & NtGlobalFlag);
|
|
|
|
//
|
|
// Serialize for here on out
|
|
//
|
|
|
|
Peb->LoaderLock = (PVOID)&LoaderLock;
|
|
if ( !RtlTryEnterCriticalSection(&LoaderLock) ) {
|
|
if ( LoaderLockInitialized ) {
|
|
RtlEnterCriticalSection(&LoaderLock);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// drop into a 30ms delay loop
|
|
//
|
|
|
|
DelayValue.QuadPart = Int32x32To64( 30, -10000 );
|
|
while ( !LoaderLockInitialized ) {
|
|
NtDelayExecution(FALSE,&DelayValue);
|
|
}
|
|
RtlEnterCriticalSection(&LoaderLock);
|
|
}
|
|
}
|
|
|
|
if (Teb->DeallocationStack == NULL) {
|
|
st = NtQueryVirtualMemory(
|
|
NtCurrentProcess(),
|
|
Teb->NtTib.StackLimit,
|
|
MemoryBasicInformation,
|
|
(PVOID)&MemInfo,
|
|
sizeof(MemInfo),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(st) ) {
|
|
LdrpInitializationFailure(st);
|
|
RtlRaiseStatus(st);
|
|
return;
|
|
}
|
|
else {
|
|
Teb->DeallocationStack = MemInfo.AllocationBase;
|
|
}
|
|
}
|
|
|
|
InitStatus = STATUS_SUCCESS;
|
|
try {
|
|
if (!Peb->Ldr) {
|
|
LdrpInLdrInit = TRUE;
|
|
#if DBG
|
|
//
|
|
// Time the load.
|
|
//
|
|
|
|
if (LdrpDisplayLoadTime) {
|
|
NtQueryPerformanceCounter(&BeginTime, NULL);
|
|
}
|
|
#endif // DBG
|
|
|
|
try {
|
|
InitStatus = LdrpInitializeProcess( Context,
|
|
SystemArgument1,
|
|
UnicodeImageName.Length ? &UnicodeImageName : NULL
|
|
);
|
|
}
|
|
except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
InitStatus = GetExceptionCode();
|
|
AlreadyFailed = TRUE;
|
|
LdrpInitializationFailure(GetExceptionCode());
|
|
}
|
|
#if DBG
|
|
if (LdrpDisplayLoadTime) {
|
|
NtQueryPerformanceCounter(&EndTime, NULL);
|
|
NtQueryPerformanceCounter(&ElapsedTime, &Interval);
|
|
ElapsedTime.QuadPart = EndTime.QuadPart - BeginTime.QuadPart;
|
|
DbgPrint("\nLoadTime %ld In units of %ld cycles/second \n",
|
|
ElapsedTime.LowPart,
|
|
Interval.LowPart
|
|
);
|
|
|
|
ElapsedTime.QuadPart = EndTime.QuadPart - InitbTime.QuadPart;
|
|
DbgPrint("InitTime %ld\n",
|
|
ElapsedTime.LowPart
|
|
);
|
|
DbgPrint("Compares %d Bypasses %d Normal Snaps %d\nSecOpens %d SecCreates %d Maps %d Relocates %d\n",
|
|
LdrpCompareCount,
|
|
LdrpSnapBypass,
|
|
LdrpNormalSnap,
|
|
LdrpSectionOpens,
|
|
LdrpSectionCreates,
|
|
LdrpSectionMaps,
|
|
LdrpSectionRelocates
|
|
);
|
|
}
|
|
#endif // DBG
|
|
|
|
if (!NT_SUCCESS(InitStatus)) {
|
|
#if DBG
|
|
DbgPrint("LDR: LdrpInitializeProcess failed - %X\n", InitStatus);
|
|
#endif // DBG
|
|
}
|
|
|
|
} else {
|
|
if ( Peb->InheritedAddressSpace ) {
|
|
InitStatus = LdrpForkProcess();
|
|
}
|
|
else {
|
|
|
|
#if defined (WX86)
|
|
if (Teb->Vdm) {
|
|
InitStatus = LdrpInitWx86(Teb->Vdm, Context, TRUE);
|
|
}
|
|
#endif
|
|
|
|
LdrpInitializeThread();
|
|
}
|
|
}
|
|
} finally {
|
|
LdrpInLdrInit = FALSE;
|
|
RtlLeaveCriticalSection(&LoaderLock);
|
|
}
|
|
|
|
NtTestAlert();
|
|
|
|
if (!NT_SUCCESS(InitStatus)) {
|
|
|
|
if ( AlreadyFailed == FALSE ) {
|
|
LdrpInitializationFailure(InitStatus);
|
|
}
|
|
RtlRaiseStatus(InitStatus);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
LdrpForkProcess( VOID )
|
|
{
|
|
NTSTATUS st;
|
|
PPEB Peb;
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
InitializeListHead( &RtlCriticalSectionList );
|
|
RtlInitializeCriticalSection( &RtlCriticalSectionLock );
|
|
|
|
InsertTailList(&RtlCriticalSectionList, &LoaderLock.DebugInfo->ProcessLocksList);
|
|
LoaderLock.DebugInfo->CriticalSection = &LoaderLock;
|
|
LoaderLockInitialized = TRUE;
|
|
|
|
st = RtlInitializeCriticalSection(&FastPebLock);
|
|
if ( !NT_SUCCESS(st) ) {
|
|
RtlRaiseStatus(st);
|
|
}
|
|
Peb->FastPebLock = &FastPebLock;
|
|
Peb->FastPebLockRoutine = (PVOID)&RtlEnterCriticalSection;
|
|
Peb->FastPebUnlockRoutine = (PVOID)&RtlLeaveCriticalSection;
|
|
Peb->InheritedAddressSpace = FALSE;
|
|
RtlInitializeHeapManager();
|
|
Peb->ProcessHeap = RtlCreateHeap( HEAP_GROWABLE, // Flags
|
|
NULL, // HeapBase
|
|
64 * 1024, // ReserveSize
|
|
4096, // CommitSize
|
|
NULL, // Lock to use for serialization
|
|
NULL // GrowthThreshold
|
|
);
|
|
if (Peb->ProcessHeap == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
NTSTATUS
|
|
LdrpInitializeProcess (
|
|
IN PCONTEXT Context OPTIONAL,
|
|
IN PVOID SystemDllBase,
|
|
IN PUNICODE_STRING UnicodeImageName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the loader for the process.
|
|
This includes:
|
|
|
|
- Initializing the loader data table
|
|
|
|
- Connecting to the loader subsystem
|
|
|
|
- Initializing all staticly linked DLLs
|
|
|
|
Arguments:
|
|
|
|
Context - Supplies an optional context buffer that will be restore
|
|
after all DLL initialization has been completed. If this
|
|
parameter is NULL then this is a dynamic snap of this module.
|
|
Otherwise this is a static snap prior to the user process
|
|
gaining control.
|
|
|
|
SystemDllBase - Supplies the base address of the system dll.
|
|
|
|
Return Value:
|
|
|
|
Status value
|
|
|
|
--*/
|
|
|
|
{
|
|
PPEB Peb;
|
|
NTSTATUS st;
|
|
PWCH p, pp;
|
|
UNICODE_STRING CurDir;
|
|
UNICODE_STRING FullImageName;
|
|
UNICODE_STRING CommandLine;
|
|
HANDLE LinkHandle;
|
|
WCHAR SystemDllPathBuffer[DOS_MAX_PATH_LENGTH];
|
|
UNICODE_STRING SystemDllPath;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
|
UNICODE_STRING Unicode;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
BOOLEAN StaticCurDir = FALSE;
|
|
ULONG i;
|
|
PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader( NtCurrentPeb()->ImageBaseAddress );
|
|
PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
|
|
ULONG ProcessHeapFlags;
|
|
RTL_HEAP_PARAMETERS HeapParameters;
|
|
NLSTABLEINFO InitTableInfo;
|
|
LARGE_INTEGER LongTimeout;
|
|
UNICODE_STRING NtSystemRoot;
|
|
|
|
#if DBG
|
|
PCHAR EventIdBuffer;
|
|
#endif // DBG
|
|
|
|
NtDllBase = SystemDllBase;
|
|
|
|
if ( NtHeader->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_NATIVE ) {
|
|
|
|
//
|
|
// Native subsystems load slower, but validate their DLLs
|
|
// This is to help CSR detect bad images faster
|
|
//
|
|
|
|
LdrpVerifyDlls = TRUE;
|
|
|
|
}
|
|
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
#if defined (_ALPHA_) && defined (WX86)
|
|
//
|
|
// for Wx86 deal with alpha's page size which is larger than x86
|
|
// This needs to be done before any code reads beyond the file headers.
|
|
//
|
|
if (NtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 &&
|
|
NtHeader->OptionalHeader.SectionAlignment < PAGE_SIZE &&
|
|
!LdrpWx86FormatVirtualImage(NtHeader,
|
|
(PVOID)NtHeader->OptionalHeader.ImageBase
|
|
))
|
|
{
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
#endif
|
|
|
|
|
|
LdrpNumberOfProcessors = Peb->NumberOfProcessors;
|
|
RtlpTimeout = Peb->CriticalSectionTimeout;
|
|
LongTimeout.QuadPart = Int32x32To64( 3600, -10000000 );
|
|
|
|
if (ProcessParameters = RtlNormalizeProcessParams(Peb->ProcessParameters)) {
|
|
FullImageName = *(PUNICODE_STRING)&ProcessParameters->ImagePathName;
|
|
CommandLine = *(PUNICODE_STRING)&ProcessParameters->CommandLine;
|
|
}
|
|
else {
|
|
RtlInitUnicodeString( &FullImageName, NULL );
|
|
RtlInitUnicodeString( &CommandLine, NULL );
|
|
}
|
|
|
|
|
|
RtlInitNlsTables(
|
|
Peb->AnsiCodePageData,
|
|
Peb->OemCodePageData,
|
|
Peb->UnicodeCaseTableData,
|
|
&InitTableInfo
|
|
);
|
|
|
|
RtlResetRtlTranslations(&InitTableInfo);
|
|
|
|
#if DBG
|
|
if (UnicodeImageName) {
|
|
DbgPrint( "LDR: Using value of 0x%08x GlobalFlag for %wZ\n",
|
|
NtGlobalFlag,
|
|
UnicodeImageName
|
|
);
|
|
}
|
|
|
|
LdrpEventIdBufferSize = sizeof( LdrpEventIdBuffer );
|
|
EventIdBuffer = (PVOID)LdrpEventIdBuffer;
|
|
|
|
RtlpCreateHeapEventId = RtlCreateEventId( &EventIdBuffer,
|
|
&LdrpEventIdBufferSize,
|
|
"CreateHeap",
|
|
4,
|
|
RTL_EVENT_FLAGS_PARAM, "", 8,
|
|
HEAP_NO_SERIALIZE, "Serialize",
|
|
HEAP_GROWABLE, "Growable",
|
|
HEAP_GENERATE_EXCEPTIONS, "Exceptions",
|
|
HEAP_ZERO_MEMORY, "ZeroInitialize",
|
|
HEAP_REALLOC_IN_PLACE_ONLY, "ReAllocInPlace",
|
|
HEAP_TAIL_CHECKING_ENABLED, "TailCheck",
|
|
HEAP_FREE_CHECKING_ENABLED, "FreeCheck",
|
|
HEAP_DISABLE_COALESCE_ON_FREE, "NoCoalesceOnFree",
|
|
RTL_EVENT_ULONG_PARAM, "HeapBase", 0,
|
|
RTL_EVENT_ULONG_PARAM, "ReserveSize", 0,
|
|
RTL_EVENT_ULONG_PARAM, "CommitSize", 0
|
|
);
|
|
|
|
RtlpDestroyHeapEventId = RtlCreateEventId( &EventIdBuffer,
|
|
&LdrpEventIdBufferSize,
|
|
"DestroyHeap",
|
|
1,
|
|
RTL_EVENT_ULONG_PARAM, "HeapBase", 0
|
|
);
|
|
|
|
RtlpAllocHeapEventId = RtlCreateEventId( &EventIdBuffer,
|
|
&LdrpEventIdBufferSize,
|
|
"AllocHeap",
|
|
4,
|
|
RTL_EVENT_ULONG_PARAM, "HeapBase", 0,
|
|
RTL_EVENT_FLAGS_PARAM, "", 8,
|
|
HEAP_NO_SERIALIZE, "Serialize",
|
|
HEAP_GROWABLE, "Growable",
|
|
HEAP_GENERATE_EXCEPTIONS, "Exceptions",
|
|
HEAP_ZERO_MEMORY, "ZeroInitialize",
|
|
HEAP_REALLOC_IN_PLACE_ONLY, "ReAllocInPlace",
|
|
HEAP_TAIL_CHECKING_ENABLED, "TailCheck",
|
|
HEAP_FREE_CHECKING_ENABLED, "FreeCheck",
|
|
HEAP_DISABLE_COALESCE_ON_FREE, "NoCoalesceOnFree",
|
|
RTL_EVENT_ULONG_PARAM, "Size", 0,
|
|
RTL_EVENT_ULONG_PARAM, "Result", 0
|
|
);
|
|
|
|
RtlpReAllocHeapEventId = RtlCreateEventId( &EventIdBuffer,
|
|
&LdrpEventIdBufferSize,
|
|
"ReAllocHeap",
|
|
6,
|
|
RTL_EVENT_ULONG_PARAM, "HeapBase", 0,
|
|
RTL_EVENT_FLAGS_PARAM, "", 8,
|
|
HEAP_NO_SERIALIZE, "Serialize",
|
|
HEAP_GROWABLE, "Growable",
|
|
HEAP_GENERATE_EXCEPTIONS, "Exceptions",
|
|
HEAP_ZERO_MEMORY, "ZeroInitialize",
|
|
HEAP_REALLOC_IN_PLACE_ONLY, "ReAllocInPlace",
|
|
HEAP_TAIL_CHECKING_ENABLED, "TailCheck",
|
|
HEAP_FREE_CHECKING_ENABLED, "FreeCheck",
|
|
HEAP_DISABLE_COALESCE_ON_FREE, "NoCoalesceOnFree",
|
|
RTL_EVENT_ULONG_PARAM, "Address", 0,
|
|
RTL_EVENT_ULONG_PARAM, "OldSize", 0,
|
|
RTL_EVENT_ULONG_PARAM, "NewSize", 0,
|
|
RTL_EVENT_ULONG_PARAM, "Result", 0
|
|
);
|
|
|
|
RtlpFreeHeapEventId = RtlCreateEventId( &EventIdBuffer,
|
|
&LdrpEventIdBufferSize,
|
|
"FreeHeap",
|
|
4,
|
|
RTL_EVENT_ULONG_PARAM, "HeapBase", 0,
|
|
RTL_EVENT_FLAGS_PARAM, "", 8,
|
|
HEAP_NO_SERIALIZE, "Serialize",
|
|
HEAP_GROWABLE, "Growable",
|
|
HEAP_GENERATE_EXCEPTIONS, "Exceptions",
|
|
HEAP_ZERO_MEMORY, "ZeroInitialize",
|
|
HEAP_REALLOC_IN_PLACE_ONLY, "ReAllocInPlace",
|
|
HEAP_TAIL_CHECKING_ENABLED, "TailCheck",
|
|
HEAP_FREE_CHECKING_ENABLED, "FreeCheck",
|
|
HEAP_DISABLE_COALESCE_ON_FREE, "NoCoalesceOnFree",
|
|
RTL_EVENT_ULONG_PARAM, "Address", 0,
|
|
RTL_EVENT_ENUM_PARAM, "Result", 2,
|
|
FALSE, "False",
|
|
TRUE, "True"
|
|
);
|
|
|
|
LdrpCreateProcessEventId = RtlCreateEventId( &EventIdBuffer,
|
|
&LdrpEventIdBufferSize,
|
|
"CreateProcess",
|
|
3,
|
|
RTL_EVENT_PUNICODE_STRING_PARAM, "ImageFilePath", 0,
|
|
RTL_EVENT_ULONG_PARAM, "ImageBase", 0,
|
|
RTL_EVENT_PUNICODE_STRING_PARAM, "CommandLine", 0
|
|
);
|
|
|
|
LdrpLoadModuleEventId = RtlCreateEventId( &EventIdBuffer,
|
|
&LdrpEventIdBufferSize,
|
|
"LoadModule",
|
|
3,
|
|
RTL_EVENT_PUNICODE_STRING_PARAM, "ImageFilePath", 0,
|
|
RTL_EVENT_ULONG_PARAM, "ImageBase", 0,
|
|
RTL_EVENT_ULONG_PARAM, "ImageSize", 0
|
|
);
|
|
|
|
LdrpUnloadModuleEventId = RtlCreateEventId( &EventIdBuffer,
|
|
&LdrpEventIdBufferSize,
|
|
"UnloadModule",
|
|
2,
|
|
RTL_EVENT_PUNICODE_STRING_PARAM, "ImageFilePath", 0,
|
|
RTL_EVENT_ULONG_PARAM, "ImageBase", 0
|
|
);
|
|
|
|
RtlLogEvent( LdrpCreateProcessEventId,
|
|
0,
|
|
&FullImageName,
|
|
Peb->ImageBaseAddress,
|
|
&CommandLine
|
|
);
|
|
#endif // DBG
|
|
|
|
|
|
ImageConfigData = RtlImageDirectoryEntryToData( Peb->ImageBaseAddress,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
|
|
&i
|
|
);
|
|
|
|
RtlZeroMemory( &HeapParameters, sizeof( HeapParameters ) );
|
|
ProcessHeapFlags = HEAP_GROWABLE | HEAP_CLASS_0;
|
|
HeapParameters.Length = sizeof( HeapParameters );
|
|
if (ImageConfigData != NULL && i == sizeof( *ImageConfigData )) {
|
|
NtGlobalFlag &= ~ImageConfigData->GlobalFlagsClear;
|
|
NtGlobalFlag |= ImageConfigData->GlobalFlagsSet;
|
|
|
|
if (ImageConfigData->CriticalSectionDefaultTimeout != 0) {
|
|
//
|
|
// Convert from milliseconds to NT time scale (100ns)
|
|
//
|
|
RtlpTimeout.QuadPart = Int32x32To64( (LONG)ImageConfigData->CriticalSectionDefaultTimeout,
|
|
-10000
|
|
);
|
|
|
|
#if DBG
|
|
DbgPrint( "LDR: Using CriticalSectionTimeout of 0x%x ms from image.\n",
|
|
ImageConfigData->CriticalSectionDefaultTimeout
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (ImageConfigData->ProcessHeapFlags != 0) {
|
|
ProcessHeapFlags = ImageConfigData->ProcessHeapFlags;
|
|
#if DBG
|
|
DbgPrint( "LDR: Using ProcessHeapFlags of 0x%x from image.\n",
|
|
ProcessHeapFlags
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (ImageConfigData->DeCommitFreeBlockThreshold != 0) {
|
|
HeapParameters.DeCommitFreeBlockThreshold = ImageConfigData->DeCommitFreeBlockThreshold;
|
|
#if DBG
|
|
DbgPrint( "LDR: Using DeCommitFreeBlockThreshold of 0x%x from image.\n",
|
|
HeapParameters.DeCommitFreeBlockThreshold
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (ImageConfigData->DeCommitTotalFreeThreshold != 0) {
|
|
HeapParameters.DeCommitTotalFreeThreshold = ImageConfigData->DeCommitTotalFreeThreshold;
|
|
#if DBG
|
|
DbgPrint( "LDR: Using DeCommitTotalFreeThreshold of 0x%x from image.\n",
|
|
HeapParameters.DeCommitTotalFreeThreshold
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (ImageConfigData->MaximumAllocationSize != 0) {
|
|
HeapParameters.MaximumAllocationSize = ImageConfigData->MaximumAllocationSize;
|
|
#if DBG
|
|
DbgPrint( "LDR: Using MaximumAllocationSize of 0x%x from image.\n",
|
|
HeapParameters.MaximumAllocationSize
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (ImageConfigData->VirtualMemoryThreshold != 0) {
|
|
HeapParameters.VirtualMemoryThreshold = ImageConfigData->VirtualMemoryThreshold;
|
|
#if DBG
|
|
DbgPrint( "LDR: Using VirtualMemoryThreshold of 0x%x from image.\n",
|
|
HeapParameters.VirtualMemoryThreshold
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// This field is non-zero if the image file that was used to create this
|
|
// process contained a non-zero value in its image header. If so, then
|
|
// set the affinity mask for the process using this value. It could also
|
|
// be non-zero if the parent process create us suspended and poked our
|
|
// PEB with a non-zero value before resuming.
|
|
//
|
|
if (Peb->ImageProcessAffinityMask) {
|
|
st = NtSetInformationProcess( NtCurrentProcess(),
|
|
ProcessAffinityMask,
|
|
&Peb->ImageProcessAffinityMask,
|
|
sizeof( Peb->ImageProcessAffinityMask )
|
|
);
|
|
if (NT_SUCCESS( st )) {
|
|
KdPrint(( "LDR: Using ProcessAffinityMask of 0x%x from image.\n",
|
|
Peb->ImageProcessAffinityMask
|
|
));
|
|
}
|
|
else {
|
|
KdPrint(( "LDR: Failed to set ProcessAffinityMask of 0x%x from image (Status == %08x).\n",
|
|
Peb->ImageProcessAffinityMask, st
|
|
));
|
|
}
|
|
}
|
|
|
|
if (RtlpTimeout.QuadPart < LongTimeout.QuadPart) {
|
|
RtlpTimoutDisable = TRUE;
|
|
}
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint( "LDR: PID: 0x%x started - '%wZ'\n",
|
|
NtCurrentTeb()->ClientId.UniqueProcess,
|
|
&CommandLine
|
|
);
|
|
}
|
|
|
|
for(i=0;i<LDRP_HASH_TABLE_SIZE;i++) {
|
|
InitializeListHead(&LdrpHashTable[i]);
|
|
}
|
|
|
|
InitializeListHead( &RtlCriticalSectionList );
|
|
RtlInitializeCriticalSection( &RtlCriticalSectionLock );
|
|
|
|
Peb->TlsBitmap = (PVOID)&TlsBitMap;
|
|
|
|
RtlInitializeBitMap (
|
|
&TlsBitMap,
|
|
&Peb->TlsBitmapBits[0],
|
|
sizeof(Peb->TlsBitmapBits) * 8
|
|
);
|
|
Peb->TlsExpansionCounter = sizeof(Peb->TlsBitmapBits) * 8;
|
|
|
|
//
|
|
// Initialize the critical section package.
|
|
//
|
|
|
|
RtlpInitDeferedCriticalSection();
|
|
|
|
|
|
InsertTailList(&RtlCriticalSectionList, &LoaderLock.DebugInfo->ProcessLocksList);
|
|
LoaderLock.DebugInfo->CriticalSection = &LoaderLock;
|
|
LoaderLockInitialized = TRUE;
|
|
|
|
//
|
|
// Initialize the stack trace data base if requested
|
|
//
|
|
|
|
#if i386
|
|
if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
|
|
PVOID BaseAddress = NULL;
|
|
ULONG ReserveSize = 2 * 1024 * 1024;
|
|
|
|
st = NtAllocateVirtualMemory( NtCurrentProcess(),
|
|
(PVOID *)&BaseAddress,
|
|
0,
|
|
&ReserveSize,
|
|
MEM_RESERVE,
|
|
PAGE_READWRITE
|
|
);
|
|
if ( NT_SUCCESS( st ) ) {
|
|
st = RtlInitializeStackTraceDataBase( BaseAddress,
|
|
0,
|
|
ReserveSize
|
|
);
|
|
if ( !NT_SUCCESS( st ) ) {
|
|
NtFreeVirtualMemory( NtCurrentProcess(),
|
|
(PVOID *)&BaseAddress,
|
|
&ReserveSize,
|
|
MEM_RELEASE
|
|
);
|
|
}
|
|
else {
|
|
NtGlobalFlag |= FLG_HEAP_VALIDATE_PARAMETERS;
|
|
}
|
|
}
|
|
}
|
|
#endif // i386
|
|
|
|
//
|
|
// Initialize the loader data based in the PEB.
|
|
//
|
|
|
|
st = RtlInitializeCriticalSection(&FastPebLock);
|
|
if ( !NT_SUCCESS(st) ) {
|
|
return st;
|
|
}
|
|
Peb->FastPebLock = &FastPebLock;
|
|
Peb->FastPebLockRoutine = (PVOID)&RtlEnterCriticalSection;
|
|
Peb->FastPebUnlockRoutine = (PVOID)&RtlLeaveCriticalSection;
|
|
|
|
RtlInitializeHeapManager();
|
|
if (NtHeader->OptionalHeader.MajorSubsystemVersion <= 3 &&
|
|
NtHeader->OptionalHeader.MinorSubsystemVersion < 51
|
|
) {
|
|
ProcessHeapFlags |= HEAP_CREATE_ALIGN_16;
|
|
}
|
|
|
|
Peb->ProcessHeap = RtlCreateHeap( ProcessHeapFlags,
|
|
NULL,
|
|
NtHeader->OptionalHeader.SizeOfHeapReserve,
|
|
NtHeader->OptionalHeader.SizeOfHeapCommit,
|
|
NULL, // Lock to use for serialization
|
|
&HeapParameters
|
|
);
|
|
if (Peb->ProcessHeap == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
NtdllBaseTag = RtlCreateTagHeap( Peb->ProcessHeap,
|
|
0,
|
|
L"NTDLL!",
|
|
L"!Process\0" // Heap Name
|
|
L"CSRSS Client\0"
|
|
L"LDR Database\0"
|
|
L"Current Directory\0"
|
|
L"TLS Storage\0"
|
|
L"DBGSS Client\0"
|
|
L"SE Temporary\0"
|
|
L"Temporary\0"
|
|
L"LocalAtom\0"
|
|
);
|
|
|
|
RtlAllocateStringRoutine = NtdllpAllocateStringRoutine;
|
|
RtlFreeStringRoutine = NtdllpFreeStringRoutine;
|
|
|
|
RtlInitializeAtomPackage( MAKE_TAG( ATOM_TAG ) );
|
|
|
|
RtlpCurdirInit();
|
|
|
|
SystemDllPath.Buffer = SystemDllPathBuffer;
|
|
SystemDllPath.Length = 0;
|
|
SystemDllPath.MaximumLength = sizeof( SystemDllPathBuffer );
|
|
RtlInitUnicodeString( &NtSystemRoot, USER_SHARED_DATA->NtSystemRoot );
|
|
RtlAppendUnicodeStringToString( &SystemDllPath, &NtSystemRoot );
|
|
RtlAppendUnicodeToString( &SystemDllPath, L"\\System32\\" );
|
|
|
|
RtlInitUnicodeString(&Unicode,L"\\KnownDlls");
|
|
InitializeObjectAttributes( &Obja,
|
|
&Unicode,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
st = NtOpenDirectoryObject(
|
|
&LdrpKnownDllObjectDirectory,
|
|
DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
|
|
&Obja
|
|
);
|
|
if ( !NT_SUCCESS(st) ) {
|
|
LdrpKnownDllObjectDirectory = NULL;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Open up the known dll pathname link
|
|
// and query its value
|
|
//
|
|
|
|
RtlInitUnicodeString(&Unicode,L"KnownDllPath");
|
|
InitializeObjectAttributes( &Obja,
|
|
&Unicode,
|
|
OBJ_CASE_INSENSITIVE,
|
|
LdrpKnownDllObjectDirectory,
|
|
NULL
|
|
);
|
|
st = NtOpenSymbolicLinkObject( &LinkHandle,
|
|
SYMBOLIC_LINK_QUERY,
|
|
&Obja
|
|
);
|
|
if (NT_SUCCESS( st )) {
|
|
LdrpKnownDllPath.Length = 0;
|
|
LdrpKnownDllPath.MaximumLength = sizeof(LdrpKnownDllPathBuffer);
|
|
LdrpKnownDllPath.Buffer = LdrpKnownDllPathBuffer;
|
|
st = NtQuerySymbolicLinkObject( LinkHandle,
|
|
&LdrpKnownDllPath,
|
|
NULL
|
|
);
|
|
NtClose(LinkHandle);
|
|
if ( !NT_SUCCESS(st) ) {
|
|
return st;
|
|
}
|
|
}
|
|
else {
|
|
return st;
|
|
}
|
|
}
|
|
|
|
if (ProcessParameters) {
|
|
|
|
//
|
|
// If the process was created with process parameters,
|
|
// than extract:
|
|
//
|
|
// - Library Search Path
|
|
//
|
|
// - Starting Current Directory
|
|
//
|
|
|
|
if (ProcessParameters->DllPath.Length) {
|
|
LdrpDefaultPath = *(PUNICODE_STRING)&ProcessParameters->DllPath;
|
|
}
|
|
else {
|
|
LdrpInitializationFailure( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
StaticCurDir = TRUE;
|
|
CurDir = ProcessParameters->CurrentDirectory.DosPath;
|
|
if (CurDir.Buffer == NULL || CurDir.Buffer[ 0 ] == UNICODE_NULL || CurDir.Length == 0) {
|
|
CurDir.Buffer = (RtlAllocateStringRoutine)( (3+1) * sizeof( WCHAR ) );
|
|
ASSERT(CurDir.Buffer != NULL);
|
|
RtlMoveMemory( CurDir.Buffer,
|
|
USER_SHARED_DATA->NtSystemRoot,
|
|
3 * sizeof( WCHAR )
|
|
);
|
|
CurDir.Buffer[ 3 ] = UNICODE_NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure the module data base is initialized before we take any
|
|
// exceptions.
|
|
//
|
|
|
|
Peb->Ldr = RtlAllocateHeap(Peb->ProcessHeap, MAKE_TAG( LDR_TAG ), sizeof(PEB_LDR_DATA));
|
|
if ( !Peb->Ldr ) {
|
|
RtlRaiseStatus(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
Peb->Ldr->Length = sizeof(PEB_LDR_DATA);
|
|
Peb->Ldr->Initialized = TRUE;
|
|
Peb->Ldr->SsHandle = NULL;
|
|
InitializeListHead(&Peb->Ldr->InLoadOrderModuleList);
|
|
InitializeListHead(&Peb->Ldr->InMemoryOrderModuleList);
|
|
InitializeListHead(&Peb->Ldr->InInitializationOrderModuleList);
|
|
|
|
//
|
|
// Allocate the first data table entry for the image. Since we
|
|
// have already mapped this one, we need to do the allocation by hand.
|
|
// Its characteristics identify it as not a Dll, but it is linked
|
|
// into the table so that pc correlation searching doesn't have to
|
|
// be special cased.
|
|
//
|
|
|
|
LdrDataTableEntry = LdrpImageEntry = LdrpAllocateDataTableEntry(Peb->ImageBaseAddress);
|
|
LdrDataTableEntry->LoadCount = (USHORT)0xffff;
|
|
LdrDataTableEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrDataTableEntry->DllBase);
|
|
LdrDataTableEntry->FullDllName = FullImageName;
|
|
LdrDataTableEntry->Flags = 0;
|
|
|
|
// p = strrchr(FullImageName, '\\');
|
|
pp = UNICODE_NULL;
|
|
p = FullImageName.Buffer;
|
|
while (*p) {
|
|
if (*p++ == (WCHAR)'\\') {
|
|
pp = p;
|
|
}
|
|
}
|
|
|
|
LdrDataTableEntry->FullDllName.Length = (USHORT)((ULONG)p - (ULONG)FullImageName.Buffer);
|
|
LdrDataTableEntry->FullDllName.MaximumLength = LdrDataTableEntry->FullDllName.Length + (USHORT)sizeof(UNICODE_NULL);
|
|
|
|
if (pp) {
|
|
LdrDataTableEntry->BaseDllName.Length = (USHORT)((ULONG)p - (ULONG)pp);
|
|
LdrDataTableEntry->BaseDllName.MaximumLength = LdrDataTableEntry->BaseDllName.Length + (USHORT)sizeof(UNICODE_NULL);
|
|
LdrDataTableEntry->BaseDllName.Buffer = RtlAllocateHeap(Peb->ProcessHeap, MAKE_TAG( LDR_TAG ),
|
|
LdrDataTableEntry->BaseDllName.MaximumLength
|
|
);
|
|
RtlMoveMemory(LdrDataTableEntry->BaseDllName.Buffer,
|
|
pp,
|
|
LdrDataTableEntry->BaseDllName.MaximumLength
|
|
);
|
|
} else {
|
|
LdrDataTableEntry->BaseDllName = LdrDataTableEntry->FullDllName;
|
|
}
|
|
LdrpInsertMemoryTableEntry(LdrDataTableEntry);
|
|
LdrDataTableEntry->Flags |= LDRP_ENTRY_PROCESSED;
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint( "LDR: NEW PROCESS\n" );
|
|
DbgPrint( " Image Path: %wZ (%wZ)\n",
|
|
&LdrDataTableEntry->FullDllName,
|
|
&LdrDataTableEntry->BaseDllName
|
|
);
|
|
DbgPrint( " Current Directory: %wZ\n", &CurDir );
|
|
DbgPrint( " Search Path: %wZ\n", &LdrpDefaultPath );
|
|
}
|
|
|
|
//
|
|
// The process references the system DLL, so map this one next. Since
|
|
// we have already mapped this one, we need to do the allocation by
|
|
// hand. Since every application will be statically linked to the
|
|
// system Dll, we'll keep the LoadCount initialized to 0.
|
|
//
|
|
|
|
LdrDataTableEntry = LdrpAllocateDataTableEntry(SystemDllBase);
|
|
LdrDataTableEntry->Flags = (USHORT)LDRP_IMAGE_DLL;
|
|
LdrDataTableEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrDataTableEntry->DllBase);
|
|
LdrDataTableEntry->LoadCount = (USHORT)0xffff;
|
|
|
|
LdrDataTableEntry->BaseDllName.Length = SystemDllPath.Length;
|
|
RtlAppendUnicodeToString( &SystemDllPath, L"ntdll.dll" );
|
|
LdrDataTableEntry->BaseDllName.Length = SystemDllPath.Length - LdrDataTableEntry->BaseDllName.Length;
|
|
LdrDataTableEntry->BaseDllName.MaximumLength = LdrDataTableEntry->BaseDllName.Length + sizeof( UNICODE_NULL );
|
|
|
|
LdrDataTableEntry->FullDllName.Buffer =
|
|
(RtlAllocateStringRoutine)( SystemDllPath.Length + sizeof( UNICODE_NULL ) );
|
|
ASSERT(LdrDataTableEntry->FullDllName.Buffer != NULL);
|
|
RtlMoveMemory( LdrDataTableEntry->FullDllName.Buffer,
|
|
SystemDllPath.Buffer,
|
|
SystemDllPath.Length
|
|
);
|
|
LdrDataTableEntry->FullDllName.Buffer[ SystemDllPath.Length / sizeof( WCHAR ) ] = UNICODE_NULL;
|
|
LdrDataTableEntry->FullDllName.Length = SystemDllPath.Length;
|
|
LdrDataTableEntry->FullDllName.MaximumLength = SystemDllPath.Length + sizeof( UNICODE_NULL );
|
|
LdrDataTableEntry->BaseDllName.Buffer = (PWSTR)
|
|
((PCHAR)(LdrDataTableEntry->FullDllName.Buffer) +
|
|
LdrDataTableEntry->FullDllName.Length -
|
|
LdrDataTableEntry->BaseDllName.Length
|
|
);
|
|
LdrpInsertMemoryTableEntry(LdrDataTableEntry);
|
|
|
|
//
|
|
// Add init routine to list
|
|
//
|
|
|
|
InsertHeadList(&Peb->Ldr->InInitializationOrderModuleList,
|
|
&LdrDataTableEntry->InInitializationOrderLinks);
|
|
|
|
//
|
|
// Inherit the current directory
|
|
//
|
|
|
|
st = RtlSetCurrentDirectory_U(&CurDir);
|
|
if (!NT_SUCCESS(st)) {
|
|
NTSTATUS ErrorStatus;
|
|
ULONG ErrorParameters[2];
|
|
ULONG ErrorResponse;
|
|
|
|
//
|
|
// Its error time...
|
|
//
|
|
|
|
ErrorParameters[0] = (ULONG)&CurDir;
|
|
ErrorParameters[1] = (ULONG)&NtSystemRoot;
|
|
|
|
ErrorStatus = NtRaiseHardError(
|
|
STATUS_BAD_CURRENT_DIRECTORY,
|
|
2,
|
|
3,
|
|
ErrorParameters,
|
|
OptionOkCancel,
|
|
&ErrorResponse
|
|
);
|
|
if ( !StaticCurDir ) {
|
|
RtlFreeUnicodeString(&CurDir);
|
|
}
|
|
if ( NT_SUCCESS(ErrorStatus) && ErrorResponse == ResponseCancel ) {
|
|
LdrpFatalHardErrorCount++;
|
|
return st;
|
|
}
|
|
else {
|
|
CurDir = NtSystemRoot;
|
|
st = RtlSetCurrentDirectory_U(&CurDir);
|
|
}
|
|
}
|
|
else {
|
|
if ( !StaticCurDir ) {
|
|
RtlFreeUnicodeString(&CurDir);
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(WX86)
|
|
|
|
//
|
|
// Load in x86 emulator for risc (Wx86.dll)
|
|
//
|
|
|
|
if (NtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) {
|
|
st = LdrpLoadWx86Dll(Context);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
st = LdrpWalkImportDescriptor(
|
|
LdrpDefaultPath.Buffer,
|
|
LdrpImageEntry
|
|
);
|
|
|
|
LdrpReferenceLoadedDll(LdrpImageEntry);
|
|
|
|
//
|
|
// Lock the loaded DLL's to prevent dlls that back link to the exe to
|
|
// cause problems when they are unloaded
|
|
//
|
|
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PLIST_ENTRY Head,Next;
|
|
|
|
Head = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
|
|
Next = Head->Flink;
|
|
|
|
while ( Next != Head ) {
|
|
Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
Entry->LoadCount = 0xffff;
|
|
Next = Next->Flink;
|
|
}
|
|
}
|
|
|
|
//
|
|
// All static DLL's are now pinned in place. No init routines have been run yet
|
|
//
|
|
|
|
LdrpLdrDatabaseIsSetup = TRUE;
|
|
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
#if DBG
|
|
DbgPrint("LDR: Initialize of image failed. Returning Error Status\n");
|
|
#endif
|
|
return st;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(LdrpInitializeTls()) ) {
|
|
return st;
|
|
}
|
|
|
|
//
|
|
// Now that all DLLs are loaded, if the process is being debugged,
|
|
// signal the debugger with an exception
|
|
//
|
|
|
|
if ( Peb->BeingDebugged ) {
|
|
DbgBreakPoint();
|
|
ShowSnaps = (BOOLEAN)(FLG_SHOW_LDR_SNAPS & NtGlobalFlag);
|
|
}
|
|
|
|
#if defined (_X86_)
|
|
if ( LdrpNumberOfProcessors > 1 ) {
|
|
LdrpValidateImageForMp(LdrDataTableEntry);
|
|
}
|
|
#endif
|
|
|
|
#if DBG
|
|
if (LdrpDisplayLoadTime) {
|
|
NtQueryPerformanceCounter(&InitbTime, NULL);
|
|
}
|
|
#endif // DBG
|
|
|
|
st = LdrpRunInitializeRoutines(Context);
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
VOID
|
|
LdrShutdownProcess (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by a process that is terminating cleanly.
|
|
It's purpose is to call all of the processes DLLs to notify them
|
|
that the process is detaching.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPEB Peb;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PDLL_INIT_ROUTINE InitRoutine;
|
|
PLIST_ENTRY Next;
|
|
|
|
//
|
|
// only unload once ! DllTerm routines might call exit process in fatal situations
|
|
//
|
|
|
|
if ( LdrpShutdownInProgress ) {
|
|
return;
|
|
}
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
if (ShowSnaps) {
|
|
UNICODE_STRING CommandLine;
|
|
|
|
CommandLine = Peb->ProcessParameters->CommandLine;
|
|
if (!(Peb->ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
|
|
CommandLine.Buffer = (PWSTR)((PCHAR)CommandLine.Buffer + (ULONG)(Peb->ProcessParameters));
|
|
}
|
|
|
|
DbgPrint( "LDR: PID: 0x%x finished - '%wZ'\n",
|
|
NtCurrentTeb()->ClientId.UniqueProcess,
|
|
&CommandLine
|
|
);
|
|
}
|
|
|
|
LdrpShutdownThreadId = NtCurrentTeb()->ClientId.UniqueThread;
|
|
LdrpShutdownInProgress = TRUE;
|
|
RtlEnterCriticalSection(&LoaderLock);
|
|
|
|
try {
|
|
|
|
//
|
|
// check to see if the heap is locked. If so, do not do ANY
|
|
// dll processing since it is very likely that a dll will need
|
|
// to do heap operations, but that the heap is not in good shape.
|
|
// ExitProcess called in a very active app can leave threads terminated
|
|
// in the middle of the heap code or in other very bad places. Checking the
|
|
// heap lock is a good indication that the process was very active when it
|
|
// called ExitProcess
|
|
//
|
|
|
|
if ( RtlpHeapIsLocked( RtlProcessHeap() )) {
|
|
#if DBG
|
|
DbgPrint( "LDR: ExitProcess called while process heap locked\n" );
|
|
#endif
|
|
;
|
|
}
|
|
else {
|
|
if ( Peb->BeingDebugged ) {
|
|
RtlValidateProcessHeaps();
|
|
}
|
|
|
|
//
|
|
// Go in reverse order initialization order and build
|
|
// the unload list
|
|
//
|
|
|
|
Next = Peb->Ldr->InInitializationOrderModuleList.Blink;
|
|
while ( Next != &Peb->Ldr->InInitializationOrderModuleList) {
|
|
LdrDataTableEntry
|
|
= (PLDR_DATA_TABLE_ENTRY)
|
|
(CONTAINING_RECORD(Next,LDR_DATA_TABLE_ENTRY,InInitializationOrderLinks));
|
|
|
|
Next = Next->Blink;
|
|
|
|
//
|
|
// Walk through the entire list looking for
|
|
// entries. For each entry, that has an init
|
|
// routine, call it.
|
|
//
|
|
|
|
if (Peb->ImageBaseAddress != LdrDataTableEntry->DllBase) {
|
|
InitRoutine = (PDLL_INIT_ROUTINE)LdrDataTableEntry->EntryPoint;
|
|
if (InitRoutine && (LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED) ) {
|
|
if (LdrDataTableEntry->Flags) {
|
|
if ( LdrDataTableEntry->TlsIndex ) {
|
|
LdrpCallTlsInitializers(LdrDataTableEntry->DllBase,DLL_PROCESS_DETACH);
|
|
}
|
|
|
|
#if defined (WX86)
|
|
if (!Wx86CurrentTib() ||
|
|
LdrpRunWx86DllEntryPoint(InitRoutine,
|
|
NULL,
|
|
LdrDataTableEntry->DllBase,
|
|
DLL_PROCESS_DETACH,
|
|
(PVOID)1
|
|
) == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
|
|
#endif
|
|
{
|
|
(InitRoutine)(LdrDataTableEntry->DllBase,DLL_PROCESS_DETACH, (PVOID)1);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the image has tls than call its initializers
|
|
//
|
|
|
|
if ( LdrpImageHasTls ) {
|
|
LdrpCallTlsInitializers(NtCurrentPeb()->ImageBaseAddress,DLL_PROCESS_DETACH);
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
RtlLeaveCriticalSection(&LoaderLock);
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
LdrShutdownThread (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by a thread that is terminating cleanly.
|
|
It's purpose is to call all of the processes DLLs to notify them
|
|
that the thread is detaching.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPEB Peb;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PDLL_INIT_ROUTINE InitRoutine;
|
|
PLIST_ENTRY Next;
|
|
#if DBG
|
|
PTEB Teb;
|
|
PVOID GuiExit;
|
|
#endif
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
RtlEnterCriticalSection(&LoaderLock);
|
|
|
|
try {
|
|
|
|
#if DBG
|
|
//
|
|
// Spare1 is set during gui server thread cleanup in
|
|
// csrsrv to catch unexpected entry into a critical section.
|
|
//
|
|
|
|
Teb = NtCurrentTeb();
|
|
GuiExit = Teb->Spare1;
|
|
Teb->Spare1 = NULL;
|
|
#endif
|
|
|
|
//
|
|
// Go in reverse order initialization order and build
|
|
// the unload list
|
|
//
|
|
|
|
Next = Peb->Ldr->InInitializationOrderModuleList.Blink;
|
|
while ( Next != &Peb->Ldr->InInitializationOrderModuleList) {
|
|
LdrDataTableEntry
|
|
= (PLDR_DATA_TABLE_ENTRY)
|
|
(CONTAINING_RECORD(Next,LDR_DATA_TABLE_ENTRY,InInitializationOrderLinks));
|
|
|
|
Next = Next->Blink;
|
|
|
|
//
|
|
// Walk through the entire list looking for
|
|
// entries. For each entry, that has an init
|
|
// routine, call it.
|
|
//
|
|
|
|
if (Peb->ImageBaseAddress != LdrDataTableEntry->DllBase) {
|
|
if ( !(LdrDataTableEntry->Flags & LDRP_DONT_CALL_FOR_THREADS)) {
|
|
InitRoutine = (PDLL_INIT_ROUTINE)LdrDataTableEntry->EntryPoint;
|
|
if (InitRoutine && (LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED) ) {
|
|
if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL) {
|
|
if ( LdrDataTableEntry->TlsIndex ) {
|
|
LdrpCallTlsInitializers(LdrDataTableEntry->DllBase,DLL_THREAD_DETACH);
|
|
}
|
|
|
|
#if defined (WX86)
|
|
if (!Wx86CurrentTib() ||
|
|
LdrpRunWx86DllEntryPoint(InitRoutine,
|
|
NULL,
|
|
LdrDataTableEntry->DllBase,
|
|
DLL_THREAD_DETACH,
|
|
NULL
|
|
) == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
|
|
#endif
|
|
{
|
|
(InitRoutine)(LdrDataTableEntry->DllBase,DLL_THREAD_DETACH, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the image has tls than call its initializers
|
|
//
|
|
|
|
if ( LdrpImageHasTls ) {
|
|
LdrpCallTlsInitializers(NtCurrentPeb()->ImageBaseAddress,DLL_THREAD_DETACH);
|
|
}
|
|
LdrpFreeTls();
|
|
|
|
} finally {
|
|
|
|
#if DBG
|
|
Teb->Spare1 = GuiExit;
|
|
#endif
|
|
|
|
RtlLeaveCriticalSection(&LoaderLock);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
LdrpInitializeThread(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by a thread that is terminating cleanly.
|
|
It's purpose is to call all of the processes DLLs to notify them
|
|
that the thread is detaching.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPEB Peb;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PDLL_INIT_ROUTINE InitRoutine;
|
|
PLIST_ENTRY Next;
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
if ( LdrpShutdownInProgress ) {
|
|
return;
|
|
}
|
|
|
|
LdrpAllocateTls();
|
|
|
|
Next = Peb->Ldr->InMemoryOrderModuleList.Flink;
|
|
while (Next != &Peb->Ldr->InMemoryOrderModuleList) {
|
|
LdrDataTableEntry
|
|
= (PLDR_DATA_TABLE_ENTRY)
|
|
(CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
|
|
|
|
//
|
|
// Walk through the entire list looking for
|
|
// entries. For each entry, that has an init
|
|
// routine, call it.
|
|
//
|
|
if (Peb->ImageBaseAddress != LdrDataTableEntry->DllBase) {
|
|
if ( !(LdrDataTableEntry->Flags & LDRP_DONT_CALL_FOR_THREADS)) {
|
|
InitRoutine = (PDLL_INIT_ROUTINE)LdrDataTableEntry->EntryPoint;
|
|
if (InitRoutine && (LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED) ) {
|
|
if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL) {
|
|
if ( LdrDataTableEntry->TlsIndex ) {
|
|
if ( !LdrpShutdownInProgress ) {
|
|
LdrpCallTlsInitializers(LdrDataTableEntry->DllBase,DLL_THREAD_ATTACH);
|
|
}
|
|
}
|
|
|
|
#if defined (WX86)
|
|
if (!Wx86CurrentTib() ||
|
|
LdrpRunWx86DllEntryPoint(InitRoutine,
|
|
NULL,
|
|
LdrDataTableEntry->DllBase,
|
|
DLL_THREAD_ATTACH,
|
|
NULL
|
|
) == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
|
|
#endif
|
|
{
|
|
if ( !LdrpShutdownInProgress ) {
|
|
(InitRoutine)(LdrDataTableEntry->DllBase,DLL_THREAD_ATTACH, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
//
|
|
// If the image has tls than call its initializers
|
|
//
|
|
|
|
if ( LdrpImageHasTls && !LdrpShutdownInProgress ) {
|
|
LdrpCallTlsInitializers(NtCurrentPeb()->ImageBaseAddress,DLL_THREAD_ATTACH);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LdrQueryImageFileExecutionOptions(
|
|
IN PUNICODE_STRING ImagePathName,
|
|
IN PWSTR OptionName,
|
|
IN ULONG Type,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT PULONG ResultSize OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString;
|
|
PWSTR pw;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle;
|
|
UNICODE_STRING KeyPath;
|
|
WCHAR KeyPathBuffer[ 128 ];
|
|
ULONG KeyValueBuffer[ 16 ];
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
|
|
ULONG ResultLength;
|
|
|
|
KeyPath.Buffer = KeyPathBuffer;
|
|
KeyPath.Length = 0;
|
|
KeyPath.MaximumLength = sizeof( KeyPathBuffer );
|
|
|
|
RtlAppendUnicodeToString( &KeyPath,
|
|
L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\"
|
|
);
|
|
|
|
UnicodeString = *ImagePathName;
|
|
pw = (PWSTR)((PCHAR)UnicodeString.Buffer + UnicodeString.Length);
|
|
UnicodeString.MaximumLength = UnicodeString.Length;
|
|
while (UnicodeString.Length != 0) {
|
|
if (pw[ -1 ] == OBJ_NAME_PATH_SEPARATOR) {
|
|
break;
|
|
}
|
|
pw--;
|
|
UnicodeString.Length -= sizeof( *pw );
|
|
}
|
|
UnicodeString.Buffer = pw;
|
|
UnicodeString.Length = UnicodeString.MaximumLength - UnicodeString.Length;
|
|
|
|
RtlAppendUnicodeStringToString( &KeyPath, &UnicodeString );
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenKey( &KeyHandle,
|
|
GENERIC_READ,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
RtlInitUnicodeString( &UnicodeString, OptionName );
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)&KeyValueBuffer;
|
|
Status = NtQueryValueKey( KeyHandle,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof( KeyValueBuffer ),
|
|
&ResultLength
|
|
);
|
|
if (Status == STATUS_BUFFER_OVERFLOW) {
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)
|
|
RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TEMP_TAG ),
|
|
sizeof( *KeyValueInformation ) +
|
|
KeyValueInformation->DataLength
|
|
);
|
|
|
|
if (KeyValueInformation == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
else {
|
|
Status = NtQueryValueKey( KeyHandle,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof( KeyValueBuffer ),
|
|
&ResultLength
|
|
);
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
if (KeyValueInformation->Type != REG_SZ) {
|
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
else {
|
|
if (Type == REG_DWORD) {
|
|
if (BufferSize != sizeof( ULONG )) {
|
|
BufferSize = 0;
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
else {
|
|
UnicodeString.Buffer = (PWSTR)&KeyValueInformation->Data;
|
|
UnicodeString.Length = (USHORT)
|
|
(KeyValueInformation->DataLength - sizeof( UNICODE_NULL ));
|
|
UnicodeString.MaximumLength = (USHORT)KeyValueInformation->DataLength;
|
|
Status = RtlUnicodeStringToInteger( &UnicodeString, 0, (PULONG)Buffer );
|
|
}
|
|
}
|
|
else {
|
|
if (KeyValueInformation->DataLength > BufferSize) {
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
else {
|
|
BufferSize = KeyValueInformation->DataLength;
|
|
}
|
|
|
|
RtlMoveMemory( Buffer, &KeyValueInformation->Data, BufferSize );
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ResultSize )) {
|
|
*ResultSize = BufferSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
NtClose( KeyHandle );
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LdrpInitializeTls(
|
|
VOID
|
|
)
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PLIST_ENTRY Head,Next;
|
|
PIMAGE_TLS_DIRECTORY TlsImage;
|
|
PLDRP_TLS_ENTRY TlsEntry;
|
|
ULONG TlsSize;
|
|
BOOLEAN FirstTimeThru = TRUE;
|
|
|
|
InitializeListHead(&LdrpTlsList);
|
|
|
|
//
|
|
// Walk through the loaded modules an look for TLS. If we find TLS,
|
|
// lock in the module and add to the TLS chain.
|
|
//
|
|
|
|
Head = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
|
|
Next = Head->Flink;
|
|
|
|
while ( Next != Head ) {
|
|
Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
Next = Next->Flink;
|
|
|
|
TlsImage = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData(
|
|
Entry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_TLS,
|
|
&TlsSize
|
|
);
|
|
|
|
//
|
|
// mark whether or not the image file has TLS
|
|
//
|
|
|
|
if ( FirstTimeThru ) {
|
|
FirstTimeThru = FALSE;
|
|
if ( TlsImage && !LdrpImageHasTls) {
|
|
RtlpSerializeHeap( RtlProcessHeap() );
|
|
LdrpImageHasTls = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( TlsImage ) {
|
|
if (ShowSnaps) {
|
|
DbgPrint( "LDR: Tls Found in %wZ at %lx\n",
|
|
&Entry->BaseDllName,
|
|
TlsImage
|
|
);
|
|
}
|
|
|
|
TlsEntry = RtlAllocateHeap(RtlProcessHeap(),MAKE_TAG( TLS_TAG ),sizeof(*TlsEntry));
|
|
if ( !TlsEntry ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Since this DLL has TLS, lock it in
|
|
//
|
|
|
|
Entry->LoadCount = (USHORT)0xffff;
|
|
|
|
//
|
|
// Mark this as having thread local storage
|
|
//
|
|
|
|
Entry->TlsIndex = (USHORT)0xffff;
|
|
|
|
TlsEntry->Tls = *TlsImage;
|
|
InsertTailList(&LdrpTlsList,&TlsEntry->Links);
|
|
|
|
//
|
|
// Update the index for this dll's thread local storage
|
|
//
|
|
|
|
|
|
#if defined(_MIPS_)
|
|
|
|
|
|
//
|
|
// Scaled index is only supported on mips since we have to worry about
|
|
// other compiler/linker vendors not being as careful with the
|
|
// field as we have been. Ours has always been 0. Who knows what
|
|
// borland/whatcom have done
|
|
//
|
|
|
|
if ( TlsEntry->Tls.Characteristics & IMAGE_SCN_SCALE_INDEX ) {
|
|
*TlsEntry->Tls.AddressOfIndex = LdrpNumberOfTlsEntries << 2;
|
|
}
|
|
else {
|
|
*TlsEntry->Tls.AddressOfIndex = LdrpNumberOfTlsEntries;
|
|
}
|
|
#else
|
|
*TlsEntry->Tls.AddressOfIndex = LdrpNumberOfTlsEntries;
|
|
#endif // _MIPS_
|
|
TlsEntry->Tls.Characteristics = LdrpNumberOfTlsEntries++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We now have walked through all static DLLs and know
|
|
// all DLLs that reference thread local storage. Now we
|
|
// just have to allocate the thread local storage for the current
|
|
// thread and for all subsequent threads
|
|
//
|
|
|
|
return LdrpAllocateTls();
|
|
}
|
|
|
|
NTSTATUS
|
|
LdrpAllocateTls(
|
|
VOID
|
|
)
|
|
{
|
|
PTEB Teb;
|
|
PLIST_ENTRY Head, Next;
|
|
PLDRP_TLS_ENTRY TlsEntry;
|
|
PVOID *TlsVector;
|
|
|
|
Teb = NtCurrentTeb();
|
|
|
|
//
|
|
// Allocate the array of thread local storage pointers
|
|
//
|
|
|
|
if ( LdrpNumberOfTlsEntries ) {
|
|
TlsVector = RtlAllocateHeap(RtlProcessHeap(),MAKE_TAG( TLS_TAG ),sizeof(PVOID)*LdrpNumberOfTlsEntries);
|
|
if ( !TlsVector ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Teb->ThreadLocalStoragePointer = TlsVector;
|
|
|
|
#if defined(_MIPS_)
|
|
|
|
//
|
|
// let mips context switch the tls vector
|
|
//
|
|
|
|
NtSetInformationThread(NtCurrentThread(),
|
|
ThreadSetTlsArrayAddress,
|
|
&TlsVector,
|
|
sizeof(TlsVector));
|
|
|
|
#endif // _MIPS_
|
|
|
|
Head = &LdrpTlsList;
|
|
Next = Head->Flink;
|
|
|
|
while ( Next != Head ) {
|
|
TlsEntry = CONTAINING_RECORD(Next, LDRP_TLS_ENTRY, Links);
|
|
Next = Next->Flink;
|
|
TlsVector[TlsEntry->Tls.Characteristics] = RtlAllocateHeap(
|
|
RtlProcessHeap(),
|
|
MAKE_TAG( TLS_TAG ),
|
|
TlsEntry->Tls.EndAddressOfRawData - TlsEntry->Tls.StartAddressOfRawData
|
|
);
|
|
if (!TlsVector[TlsEntry->Tls.Characteristics] ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: TlsVector %x Index %d = %x copied from %x to %x\n",
|
|
TlsVector,
|
|
TlsEntry->Tls.Characteristics,
|
|
&TlsVector[TlsEntry->Tls.Characteristics],
|
|
TlsEntry->Tls.StartAddressOfRawData,
|
|
TlsVector[TlsEntry->Tls.Characteristics]
|
|
);
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
TlsVector[TlsEntry->Tls.Characteristics],
|
|
(PVOID)TlsEntry->Tls.StartAddressOfRawData,
|
|
TlsEntry->Tls.EndAddressOfRawData - TlsEntry->Tls.StartAddressOfRawData
|
|
);
|
|
|
|
//
|
|
// Do the TLS Callouts
|
|
//
|
|
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
LdrpFreeTls(
|
|
VOID
|
|
)
|
|
{
|
|
PTEB Teb;
|
|
PLIST_ENTRY Head, Next;
|
|
PLDRP_TLS_ENTRY TlsEntry;
|
|
PVOID *TlsVector;
|
|
|
|
Teb = NtCurrentTeb();
|
|
|
|
TlsVector = Teb->ThreadLocalStoragePointer;
|
|
|
|
if ( TlsVector ) {
|
|
Head = &LdrpTlsList;
|
|
Next = Head->Flink;
|
|
|
|
while ( Next != Head ) {
|
|
TlsEntry = CONTAINING_RECORD(Next, LDRP_TLS_ENTRY, Links);
|
|
Next = Next->Flink;
|
|
|
|
//
|
|
// Do the TLS callouts
|
|
//
|
|
|
|
if ( TlsVector[TlsEntry->Tls.Characteristics] ) {
|
|
RtlFreeHeap(
|
|
RtlProcessHeap(),
|
|
0,
|
|
TlsVector[TlsEntry->Tls.Characteristics]
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
RtlFreeHeap(
|
|
RtlProcessHeap(),
|
|
0,
|
|
TlsVector
|
|
);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
LdrpCallTlsInitializers(
|
|
PVOID DllBase,
|
|
ULONG Reason
|
|
)
|
|
{
|
|
PIMAGE_TLS_DIRECTORY TlsImage;
|
|
ULONG TlsSize;
|
|
PIMAGE_TLS_CALLBACK *CallBackArray;
|
|
PIMAGE_TLS_CALLBACK InitRoutine;
|
|
|
|
TlsImage = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData(
|
|
DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_TLS,
|
|
&TlsSize
|
|
);
|
|
|
|
|
|
try {
|
|
if ( TlsImage ) {
|
|
CallBackArray = TlsImage->AddressOfCallBacks;
|
|
if ( CallBackArray ) {
|
|
if (ShowSnaps) {
|
|
DbgPrint( "LDR: Tls Callbacks Found. Imagebase %lx Tls %lx CallBacks %lx\n",
|
|
DllBase,
|
|
TlsImage,
|
|
CallBackArray
|
|
);
|
|
}
|
|
|
|
while(*CallBackArray){
|
|
InitRoutine = *CallBackArray++;
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint( "LDR: Calling Tls Callback Imagebase %lx Function %lx\n",
|
|
DllBase,
|
|
InitRoutine
|
|
);
|
|
}
|
|
|
|
#if defined (WX86)
|
|
if (!Wx86CurrentTib() ||
|
|
LdrpRunWx86DllEntryPoint(
|
|
(PDLL_INIT_ROUTINE)InitRoutine,
|
|
NULL,
|
|
DllBase,
|
|
Reason,
|
|
NULL
|
|
) == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
|
|
#endif
|
|
{
|
|
(InitRoutine)(DllBase,Reason,0);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
;
|
|
}
|
|
}
|