Copyright © Microsoft Corporation. This document is an archived reproduction of a version originally published by Microsoft. It may have slight formatting modifications for consistency and to improve readability.
Figure 1   RunInit.cpp

//=============================================================================
// Matt Pietrek, September 1999 Microsoft Systems Journal
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (Windows NT 4.0, SP3)
// bImplicitLoad parameter is nonzero when LdrpRunInitializeRoutines is
// called for the first time in a process (that is, when the implicitly
// linked modules are initialized.
// On subsequent invocations (caused by calls to LoadLibrary), bImplicitLoad
// is 0.
//=============================================================================

#include <ntexapi.h>    // For HardError defines near the end

// Global symbols (name is accurate, and comes from NTDLL.DBG)
//  _NtdllBaseTag
//  _ShowSnaps
//  _SaveSp
//  _CurSp
//  _LdrpInLdrInit
//  _LdrpFatalHardErrorCount
//  _LdrpImageHasTls

NTSTATUS
LdrpRunInitializeRoutines( DWORD bImplicitLoad )
{
    // Get the number of modules that *might* need to be initialized.  Some
    // of the modules may already have been initialized.

    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();

    if ( nRoutinesToRun )
    {
        // If there are any init routines, allocate memory for an array
        // containing information about each module
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
                                          _NtdllBaseTag + 0x60000,
                                          nRoutinesToRun * 4 );
                            
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
            return STATUS_NO_MEMORY;
    }
    else
        pInitNodeArray = 0;

    // The Process Environment Block (Peb), keeps a pointer to the linked
    // list of modules that have just been loaded.  Get a pointer to this info
    //
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
        
    if ( _ShowSnaps )
    {
        _DbgPrint( "LDR: Real INIT LIST\n" );
    }

    nModulesInitedSoFar = 0;

    if ( pCurrNode != ModuleLoaderInfoHead )
    {
        // Iterate through the linked list
        //
        while ( pCurrNode != ModuleLoaderInfoHead )
        {
            ModuleLoaderInfo  pModuleLoaderInfo;
            
            // Apparently the next node pointer is 0x10 bytes inside
            // the ModuleLoaderInfo structure
            //
            pModuleLoaderInfo = &NextNode - 0x10;
            
            // This doesn't seem to do anything...
            localVar3C = pModuleLoaderInfo;         

            // Determine if the module has already been initialized.  If so,
            // skip over it.
            //
            // X_LOADER_SAW_MODULE = 0x40
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
            {
                // This module hasn't previously been initialized.  Check
                // to see if it has an EntryPoint to call.
                //
                if ( pModuleLoaderInfo->EntryPoint )
                {
                    // This previously uninitialized module has an entry
                    // point.  Add it to the array of modules that will
                    // be called to initialize later in this routine.
                    //
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;

                    // If ShowSnaps is nonzero, spit out the module path
                    // and the entry point address for the module. For example:
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
                    if ( _ShowSnaps )
                    {
                        _DbgPrint(  "%wZ init routine %x\n",
                                    &pModuleLoaderInfo->24,
                                    pModuleLoaderInfo->EntryPoint );
                    }

                    nModulesInitedSoFar++;
                }
            }

            // Set the X_LOADER_SAW_MODULE flag for this module.  Note that
            // the module hasn't actually been initialized.  Rather, it will
            // be by the time this routine returns.
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;

            // Advance to the next node in the module list
            pCurrNode = pCurrNode->pNext
        }
    }
    else
    {
        pModuleLoaderInfo = localVar3C;     // May not be initialized???
    }
    
    if ( 0 == pInitNodeArray )
        return STATUS_SUCCESS;
 
    // At this point, pInitNodeArray contains an array of pointers to
    // modules that haven't yet seen the DLL_PROCESS_ATTACH notification.
    // It's now time to start calling the initialization routines.
    //  
    try     // Wrap all this in a try block, in case the init routine faults
    {
        nModulesInitedSoFar = 0;  // Start at array element 0

        // Begin iterating through the module array
        //
        while ( nModulesInitedSoFar < nRoutinesToRun )
        {
            // Get a pointer to the module's info out of the array
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];

            // This doesn't seem to do anything...
            localVar3C = pModuleLoaderInfo;
            
            nModulesInitedSoFar++;
                
            // Store init routine address in a local variable
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
            
            fBreakOnDllLoad = 0;    // Default is to not break on load

            // If this process is a debuggee, check to see if the loader
            // should break into a debugger before calling the initialization.
            //
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
            // returns. IsDebuggerPresent is a Windows NT-only API.
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
            {
                LONG retCode;

                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
                // Windows NT\CurrentVersion\Image File Execution Options"
                // registry key.  If a a subkey entry with the name of
                // the executable exists, check for the BreakOnDllLoad value.
                          retCode = _LdrQueryImageFileExecutionOptions(
                          pModuleLoaderInfo->pwszDllName, "BreakOnDllLoad",
                          REG_DWORD, &fBreakOnDllLoad,
                          sizeof(DWORD), 0 );

                // If reg value not found (usually the case), then don't
                // break on this DLL init
                if ( retCode <= STATUS_SUCCESS )
                    fBreakOnDllLoad = 0;
            }
            
            if ( fBreakOnDllLoad )
            {           
                if ( _ShowSnaps )
                {
                    // Inform the debug output stream of the module name
                    // and the init routine address before actually breaking
                    // into the debugger

                    _DbgPrint(  "LDR: %wZ loaded.",
                                &pModuleLoaderInfo->pModuleLoaderInfo );
                    
                    _DbgPrint(  "- About to call init routine at %lx\n",
                                pfnInitRoutine )
                }
                
                // Break into the debugger                              
                _DbgBreakPoint();   // An INT 3, followed by a RET
            }
            else if ( _ShowSnaps && pfnInitRoutine )
            {
                // Inform the debug output stream of the module name
                // and the init routine address before calling it               
                _DbgPrint(  "LDR: %wZ loaded.",
                            pModuleLoaderInfo->pModuleLoaderInfo );

                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
            }
                    
            if ( pfnInitRoutine )
            {
                // Set flag indicating that the DLL_PROCESS_ATTACH notification
                // has been sent to this DLL
                // (Shouldn't this come *after* the actual call?)
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;

                // If there's Thread Local Storage (TLS) for this module,
                // call the TLS init functions.  *** NOTE *** This only
                // occurs during the first time this code is called (when
                // implicitly loaded DLLs are initialized).  Dynamically
                // loaded DLLs shouldn't use TLS declared vars, as per the
                // SDK documentation
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
                {
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
                                                DLL_PROCESS_ATTACH );
                }
                
                hModDLL = pModuleLoaderInfo->hModDLL

                MOV     ESI,ESP // Save off the ESP register into ESI
                
                MOV     EDI,DWORD PTR [pfnInitRoutine]
                        // Load EDI with module's entry point                

                // In C++ code, the following ASM would look like:
                // initRetValue =
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
                
                PUSH    DWORD PTR [bImplicitLoad]
                
                PUSH    DLL_PROCESS_ATTACH
                
                PUSH    DWORD PTR [hModDLL]
                
                CALL    EDI     // Call the init routine.  Excellent point
                                // to set a breakpoint.  Stepping into this
                                // call will take you to the DLL's entry point
                
                MOV     BYTE PTR [initRetValue],AL  // Save the return value
                                                    // from the entry point

                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns

                MOV     ESP,ESI     // Restore ESP to value before the call

                // Check ESP (stack pointer) after the call.  If it's not
                // the same as the ESP value before the call, the DLL's
                // init routine didn't clean up the stack properly.  For
                // example, it's entry routine may have been defined
                // improperly.  Although this rarely happens, if it does,
                // let the user know and ask if they want to continue.
                if ( _CurSP != _SavSP )
                {
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;

                    hardErrorRetCode = _NtRaiseHardError(
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
                            1,  // Number of parameters
                            1,  // UnicodeStringParametersMask,
                            &hardErrorParam,
                            OptionYesNo,    // Let user decide
                            &hardErrorResponse );
                                            
                    if ( _LdrpInLdrInit )
                        _LdrpFatalHardErrorCount++;

                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
                        &&  (ResponseYes == hardErrorResponse) )
                    {
                        return STATUS_DLL_INIT_FAILED;
                    }
                }

                // If the DLL's entry point returned 0 (failure), tell the user
                if ( 0 == initRetValue )
                {
                    DWORD hardErrorParam2;
                    DWORD hardErrorResponse2;
                                        
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
                    
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
                                        1,  // Number of parameters
                                        1,  // UnicodeStringParametersMask
                                        &hardErrorParam2,
                                        OptionOk,   // OK is only response
                                        &hardErrorResponse2 );
                                                            
                    if ( _LdrpInLdrInit )
                        _LdrpFatalHardErrorCount++;

                    return STATUS_DLL_INIT_FAILED;
                }
            }
        }

        // If the EXE itself has TLS declared vars, call the init routines.
        // See the comment for the previous call to _LdrpCallTlsInitializers
        // for more details.
        //      
        if ( _LdrpImageHasTls && bImplicitLoad )
        {
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
                                        DLL_PROCESS_ATTACH );
        }
    }
    __finally
    {
        // Before exiting the routine, make sure that the memory allocated
        // at the beginning is freed
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
    }

    return STATUS_SUCCESS;
}

Figure 6   ShowSnaps Output from CALC.EXE


## Matt's comments denoted by ##'s
Loaded 'C:\WINNT\system32\CALC.EXE', no matching symbolic information found.
Loaded symbols for 'C:\WINNT\system32\ntdll.dll'
LDR: PID: 0x3a started - '"C:\WINNT\system32\CALC.EXE"'
LDR: NEW PROCESS
     Image Path: C:\WINNT\system32\CALC.EXE (CALC.EXE)
     Current Directory: C:\WINNT\system32
     Search Path: C:\WINNT\system32;.;C:\WINNT\System32;C:\WINNT\system;...
LDR: SHELL32.dll used by CALC.EXE
Loaded 'C:\WINNT\system32\SHELL32.DLL', no matching symbolic information found.
LDR: ntdll.dll used by SHELL32.dll
LDR: Snapping imports for SHELL32.dll from ntdll.dll
LDR: KERNEL32.dll used by SHELL32.dll
Loaded symbols for 'C:\WINNT\system32\KERNEL32.DLL'
LDR: ntdll.dll used by KERNEL32.dll
LDR: Snapping imports for KERNEL32.dll from ntdll.dll
LDR: Snapping imports for SHELL32.dll from KERNEL32.dll
LDR: LdrLoadDll, loading NTDLL.dll from 
LDR: LdrGetProcedureAddress by NAME - RtlEnterCriticalSection
LDR: LdrLoadDll, loading NTDLL.dll from 
LDR: LdrGetProcedureAddress by NAME - RtlDeleteCriticalSection

//.... additional output omitted

LDR: GDI32.dll used by SHELL32.dll
Loaded symbols for 'C:\WINNT\system32\GDI32.DLL'
LDR: ntdll.dll used by GDI32.dll
LDR: Snapping imports for GDI32.dll from ntdll.dll
LDR: KERNEL32.dll used by GDI32.dll
LDR: Snapping imports for GDI32.dll from KERNEL32.dll
LDR: USER32.dll used by GDI32.dll
Loaded symbols for 'C:\WINNT\system32\USER32.DLL'
LDR: ntdll.dll used by USER32.dll
LDR: Snapping imports for USER32.dll from ntdll.dll
LDR: KERNEL32.dll used by USER32.dll
LDR: Snapping imports for USER32.dll from KERNEL32.dll
LDR: LdrLoadDll, loading NTDLL.dll from 
LDR: LdrGetProcedureAddress by NAME - RtlSizeHeap
LDR: LdrLoadDll, loading NTDLL.dll from 
LDR: LdrGetProcedureAddress by NAME - RtlReAllocateHeap
LDR: LdrLoadDll, loading NTDLL.dll from 
LDR: LdrGetProcedureAddress by NAME - RtlFreeHeap
LDR: LdrLoadDll, loading NTDLL.dll from 
LDR: LdrGetProcedureAddress by NAME - RtlAllocateHeap

//.... additional output omitted

## Note loader looking for and verifying "bound" DLL imports in COMCTL32
Loaded 'C:\WINNT\system32\COMCTL32.DLL', no matching symbolic information found.
LDR: COMCTL32.dll bound to ntdll.dll
LDR: COMCTL32.dll has correct binding to ntdll.dll
LDR: COMCTL32.dll bound to GDI32.dll
LDR: COMCTL32.dll has correct binding to GDI32.dll
LDR: COMCTL32.dll bound to KERNEL32.dll
LDR: COMCTL32.dll has correct binding to KERNEL32.dll
LDR: COMCTL32.dll bound to ntdll.dll via forwarder(s) from KERNEL32.dll
LDR: COMCTL32.dll has correct binding to ntdll.dll
LDR: COMCTL32.dll bound to USER32.dll
LDR: COMCTL32.dll has correct binding to USER32.dll
LDR: COMCTL32.dll bound to ADVAPI32.dll
LDR: COMCTL32.dll has correct binding to ADVAPI32.dll

//.... additional output omitted

LDR: Refcount   COMCTL32.dll (1)
LDR: Refcount   GDI32.dll (3)
LDR: Refcount   KERNEL32.dll (6)
LDR: Refcount   USER32.dll (4)
LDR: Refcount   ADVAPI32.dll (5)
LDR: Refcount   KERNEL32.dll (7)
LDR: Refcount   GDI32.dll (4)
LDR: Refcount   USER32.dll (5)

## List of implicit link DLLs to be init'ed.
LDR: Real INIT LIST
     C:\WINNT\system32\KERNEL32.dll init routine 77f01000
     C:\WINNT\system32\RPCRT4.dll init routine 77e1b6d5
     C:\WINNT\system32\ADVAPI32.dll init routine 77dc1000
     C:\WINNT\system32\USER32.dll init routine 77e78037
     C:\WINNT\system32\COMCTL32.dll init routine 71031a18
     C:\WINNT\system32\SHELL32.dll init routine 77c41094

## Beginning of actual calls to implicitly linked DLL init routines
LDR: KERNEL32.dll loaded. - Calling init routine at 77f01000
LDR: RPCRT4.dll loaded. - Calling init routine at 77e1b6d5
LDR: ADVAPI32.dll loaded. - Calling init routine at 77dc1000
LDR: USER32.dll loaded. - Calling init routine at 77e78037

## USER32 does AppInit_DLLs thing, so static inits temporarily interrupted
## In this case, "globaldll.dll" is LoadLibrary'ed from USER32 init code
LDR: LdrLoadDll, loading c:\temp\globaldll.dll from C:\WINNT\system32;.;
LDR: Loading (DYNAMIC) c:\temp\globaldll.dll
Loaded 'C:\TEMP\GlobalDLL.dll', no matching symbolic information found.
LDR: KERNEL32.dll used by globaldll.dll

//.... additional output omitted

LDR: Real INIT LIST
     c:\temp\globaldll.dll init routine 10001310
LDR: globaldll.dll loaded. - Calling init routine at 10001310

## Now back to calling the inits of the statically linked DLLs
LDR: COMCTL32.dll loaded. - Calling init routine at 71031a18
LDR: LdrGetDllHandle, searching for USER32.dll from 
LDR: LdrGetProcedureAddress by NAME - GetSystemMetrics
LDR: LdrGetProcedureAddress by NAME - MonitorFromWindow
LDR: SHELL32.dll loaded. - Calling init routine at 77c41094
//.... additional output omitted

Figure 7   TLSInit.cpp


void _LdrpCallTlsInitializers( HMODULE hModule, DWORD fdwReason )
{
    PIMAGE_TLS_DIRECTORY pTlsDir;
    DWORD size

    // Look up the TLS directory in the IMAGE_OPTIONAL_HEADER.DataDirectory
    pTlsDir = _RtlImageDirectoryEntryToData(hModule,
                                            1,
                                            IMAGE_DIRECTORY_ENTRY_TLS,
                                            &size );

    __try   // Protect all this code with a try/catch block
    {                                   
        if ( pTlsDir->AddressOfCallbacks )
        {   
            if ( _ShowSnaps )   // diagnostic output
            {
                _DbgPrint(  "LDR: Tls Callbacks Found. "
                            "Imagebase %lx Tls %lx CallBacks %lx\n",
                            hModule, TlsDir, pTlsDir->AddressOfCallbacks );
            }
            
            // Get pointer to beginning of array of TLS callback addresses
            PVOID * pCallbacks = pTlsDir->AddressOfCallbacks;
                        
            while ( *pCallbacks )   // Iterate through each array entry
            {
                PIMAGE_TLS_CALLBACK pTlsCallback = *pCallbacks;
                pCallbacks++;

                if ( _ShowSnaps )   // More diagnostic output
                {
                    _DbgPrint(  "LDR: Calling Tls Callback "
                                "Imagebase %lx Function %lx\n",
                                hModule, pTlsCallback );
                }

                // Make the actual call                         
                pTlsCallback( hModule, fdwReason, 0 );              
            }
        }
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
    }   
}